Software

Global Defines and Functions

Here datatypes are declared globally, including byte, word, and double-word aliases, and the IP (and later TCP and UDP) header structure. Two swapping functions are also declared. The byte order of the C compiler is big-endian; in SLIP it is little endian. Therefore byte-swapping functions (for 16-bit values and 32-bit values) are defined.

<global.h>

#ifndef __GLOBAL_H__
#define __GLOBAL_H__

#define BIT bit
#define BYTE unsigned char
#define WORD unsigned int
#define DWORD unsigned long

struct iphdr {
        BYTE    ihlver;
        BYTE    tos;
        WORD    tot_len;
        WORD    id;
        WORD    frag_off;
        BYTE    ttl;
        BYTE    protocol;
        WORD    check;
        DWORD   saddr;
        DWORD   daddr;
        /*The options start here. */
};
   
extern WORD byteswap16(WORD word);
extern DWORD byteswap32(DWORD dword);

#endif
  

<global.c>

#include "global.h"

union swap_union{
  BYTE b[4];
  WORD w;
  DWORD d;
} ; 

WORD byteswap16(WORD word) {
  BYTE hold;
  union swap_union u;       
  u.w=word;
  hold=u.b[0];
  u.b[0]=u.b[1];
  u.b[1]=hold;
  return u.w;
}

DWORD byteswap32(DWORD dword) { 
  BYTE hold;
  union swap_union u;
  u.d=dword;        
  hold=u.b[0];
  u.b[0]=u.b[3];
  u.b[3]=hold;
  hold=u.b[1];
  u.b[1]=u.b[2];
  u.b[2]=hold;
  return u.d;
}
  

Queue Data Structure

These files define the queue data structure and the functions for using it. The queue requires that you configure it manually, which is not particularly difficult. One important thing to note is that you must supply your own data buffer for the queue. queue_enqueue() enqueues a byte on Q, returning whether it was successful. queue_dequeue() dequeues a byte from Q, returning its value. If the dequeue operation was unsuccessful, it returns zero. The length member of the queue data structure should therefore be checked before a dequeue operation. head refers to the index to place the next byte, and should be initialized to zero and not modified outside of the queue functions. maxsize refers to the maximum number of bytes in the queue, and should be set to a number less than or equal to the size of data. length refers to the number of bytes presently in the queue. It should be initialized to zero and not modified afterwards.

<queue.h>

#ifndef __QUEUE_H__
#define __QUEUE_H__
      
#include "global.h"
      
typedef struct _queue {
  WORD head;
  WORD maxsize;
  WORD length;
  BYTE* data;
} queue;
       
BYTE queue_enqueue(queue *Q, BYTE value);
BYTE queue_dequeue(queue *Q);

#endif //__QUEUE_H__
  

<queue.c>

#include "queue.h"

BYTE queue_enqueue(queue *Q, BYTE value){
  if(Q->length==Q->maxsize) return 0;
  Q->length++;
  Q->data[Q->head++]=value;
  while(Q->head>=Q->maxsize)Q->head-=Q->maxsize;
  return 1;
}          

BYTE queue_dequeue(queue *Q) {
  WORD ind;
  if(Q->length==0) return 0;
  ind=Q->head-Q->length;
  if(Q->headlength){ind+=Q->maxsize;}
  Q->length--;
  return Q->data[ind];
}    

  

Sample Code

//1. enqueues several items
//2. dequeues them 1/2 sec at a time, outputting them to LEDs
//3. waits for button press, then repeats
#include 
#include 
#include "global.h"
#include "queue.h"
  
#define queuesize 10

BYTE qdata[queuesize];
queue queue1; 

void main(void) {
  //pointer to Q1
  queue *Q1; 
  Q1=&queue1;

  //setup LEDs
  DDRC=0xFF;
        
  //setup buttons
  DDRD=0x00;
  PORTD=0xFF;
         
  
  //initialize the queue  
  Q1->head=0;
  Q1->maxsize=queuesize;
  Q1->length=0;
  Q1->data=qdata;
                
  while(1) {  
	//enqueue bytes
    queue_enqueue(Q1,1);
    queue_enqueue(Q1,2);
    queue_enqueue(Q1,0);
    queue_enqueue(Q1,3);
    queue_enqueue(Q1,5);
    queue_enqueue(Q1,9);
    queue_enqueue(Q1,0);
    queue_enqueue(Q1,7);
    queue_enqueue(Q1,2);
    queue_enqueue(Q1,1);
    queue_enqueue(Q1,3);
    queue_enqueue(Q1,3); //this one does not get enqueued (11th member)
    
    while(Q1->length>0) {
      PORTC=~queue_dequeue(Q1); // will output 1 2 0 3 5 9 0 7 2 1 3 3 in binary to LEDs
      delay_ms(500);
    }              
    
    PORTC=~queue_dequeue(Q1); // will output 0
    delay_ms(500);

    //wait for button press
    while(PIND==0xFF);
  }
}
  

Dataflash/SPI interface

These files define macros for using the SPI interface, constants for the DataFlash, aliases for pin names, and functions for accessing the dataflash and allocating pages in it. DF_init() initializes the SPI and the heap for dynamic allocation.

The SPI macros are as follows:

The DataFlash constants are taken from this page. They represent the instructions that can be sent to the DataFlash. The DataFlash functions handle all of this. DF_wrCue cues the dataflash to a particular point in memory for writing. It must be called before trying to write to the dataflash. DF_rdCue similarly cues the dataflash for reading. The hierarchy is Chunk(there are 256), Page(4 per chunk), and Byte (264 per page).

DF_puts (for SRAM) and DF_putsf (for Flash) can be used to write to the dataflash, but it should be noted that only 264 bytes can be written, then the dataflash must be re-cued to the next page or chunk.

DF_gets retrieves a string of bytes from the dataflash. It can be used to retrieve as many bytes as desired.

After calling DF_puts[f], the dataflash must be re-cued for writing; as it must be for reading after calling DF_gets. To get around this, I added a continuous-write feature and a continuous-read feature.

DF_startWr starts a write to the dataflash. DF_puts and DF_putsf can be called multiple times, interchangeably, and the string will be stored at the end of the last stored string. After the writing is complete, a call to DF_finishWr will complete the write. Writing is still limited to 264 bytes per page, however. Similarly, after calling DF_startRd, DF_gets can be called multiple times to retrieve consecutive strings. DF_finishRd ends the continuous read.

Continuous writes and continuous reads cannot be executed simultaneously. After calling DF_startWr, DF_finishWr must be called before trying to read, and vice versa.

Also, it is VERY important is that after a write, 20 ms be allowed to elapse, SPIWAITREADY be called, or DF_ready return nonzero, before starting a new write. This is because after a write is completed, the dataflash still needs to program its write buffer into memory. Reads can still be done during this time.

<dflash.h>

#ifndef __DFLASH_H__
#define __DFLASH_H__

#include "global.h"

#define CS PORTB.4
#define MOSI PORTB.5
#define MISO PINB.6
#define SCK PORTB.7
  
#define SPISEND(x) SPDR=x;while(!(SPSR&0x80))
#define SPIGET(x) SPISEND(0x00);x=SPDR
#define SPIPAUSE CS=1;#asm("nop");#asm("nop");CS=0 
#define SPIGETSTAT(x) SPISEND(0xD7);SPIGET(x);
#define SPIWAITREADY(x) do{SPIPAUSE;SPIGETSTAT(x);}while(!(x&0x80))

#define ARRAYREAD1 0x68
#define ARRAYREAD2 0xE8
#define PAGEREAD1 0x52
#define PAGEREAD2 0xD2
#define BUF1READ1 0x54
#define BUF1READ2 0xD4
#define BUF2READ1 0x56
#define BUF2READ2 0xD6
#define STATREAD1 0x57
#define STATREAD2 0xD7

#define BUF1WRITE 0x84
#define BUF2WRITE 0x87
#define BUF1PROGE 0x83
#define BUF2PROGE 0x86
#define BUF1PROG  0x88
#define BUF2PROG  0x89
#define EPAGE     0x81
#define EBLOCK    0x50
#define MEMWRITE1 0x82
#define MEMWRITE2 0x85

#define BUF1TRANS 0x53
#define BUF2TRANS 0x55
#define PAGECOMP1 0x60
#define PAGECOMP2 0x61
#define AUTORW1   0x58
#define AUTORW2   0x59
                      
void DF_init(void);
BYTE DF_wasError(void);

BYTE DF_alloc(void);
void DF_dealloc(BYTE);
      
void DF_wrCue(BYTE DF_chunk, BYTE DF_page, WORD DF_byte);
void DF_rdCue(BYTE DF_chunk, BYTE DF_page, WORD DF_byte);

void DF_puts(char* data, BYTE len);
void DF_putsf(char flash *data, BYTE len);

void DF_gets(char* data, BYTE len);

void DF_startRd(void); //start and end a write operation
void DF_finishRd(void); //if not used, function call treated
			//as single write

void DF_startWr(void);
void DF_finishWr(void);

BYTE DF_ready(void); //OK to start new write?

#endif //__DFLASH_H__
  

<dflash.c>

#include "dflash.h"
  
BYTE DF_heap[256]; 
BYTE DF_error;
WORD DF_numfree;

BYTE DF_wrChunk;
BYTE DF_rdChunk;

BYTE DF_wrPage;
BYTE DF_rdPage;

WORD DF_wrByte;
WORD DF_rdByte;
   
BYTE DF_contRd;
BYTE DF_contWr;

void DF_init() {
  BYTE i;  
  DDRB=0b10110000;
  SPCR=0b01011100;
  SPSR|=0x01;
  DF_numfree=256;
  DF_error=0;
  DF_wrChunk=0;
  DF_rdChunk=0;
  DF_wrPage=0;
  DF_rdPage=0;
  DF_wrByte=0;
  DF_rdByte=0;
  DF_contRd=0;
  DF_contWr=0;
  i=0;
  while(1) {
    DF_heap[i]=i++;
    if(i==0) break;
  } 
  CS=1; //~chip select high     
}              

BYTE DF_wasError() {
  if(DF_error==0) return 0;
  DF_error=0; 
  return 1;
}

BYTE DF_alloc() {
  BYTE ret;
  WORD i, c1, c2; 
  DF_error=0;
  if(DF_numfree==0){DF_error=1;return 0;}
  DF_numfree--;   
  ret=DF_heap[0];
  DF_heap[0]=DF_heap[DF_numfree];
  i=0;
  while(i0)while(DF_heap[0]==ret) DF_alloc();
	//remove any duplicates (i.e. that have been
	//  deallocated more than once)
  return ret;
}            

void DF_dealloc(BYTE addr) {
  BYTE i, p, tmp;
  DF_error=0;
  if(DF_numfree==256) {DF_error=1;return;}
  i=DF_numfree;
  DF_heap[DF_numfree++]=addr;
  while(i>0) {
    p=i>>1;
    if(DF_heap[i]>5);
  byte2=(DF_wrChunk<<3)|(DF_wrPage<<1)|(DF_wrByte>>8);
  byte3=DF_wrByte;
  DF_error=0;
  DF_contWr=1;
  if(!DF_ready()) {DF_error=1; return;}
  SPIPAUSE;
  SPISEND(BUF1TRANS); 
  SPISEND(byte1);
  SPISEND(byte2);
  SPISEND(byte3); 
  SPIWAITREADY(statreg);
  SPIPAUSE;
  SPISEND(BUF1WRITE);  
  SPISEND(byte1);
  SPISEND(byte2);
  SPISEND(byte3);
}

void DF_finishWr(void) { 
  BYTE byte1, byte2, byte3, statreg;
  byte1=(DF_wrChunk>>5);
  byte2=(DF_wrChunk<<3)|(DF_wrPage<<1)|(DF_wrByte>>8);
  byte3=DF_wrByte;
  DF_contWr=0;
  DF_error=0;  
  SPIPAUSE;
  SPIWAITREADY(statreg);
  SPIPAUSE;
  SPISEND(BUF1PROGE);
  SPISEND(byte1);
  SPISEND(byte2);
  SPISEND(byte3); 
  CS=1;
}

void DF_startRd(void) {
  BYTE byte1, byte2, byte3, statreg;
  byte1=(DF_rdChunk>>5);
  byte2=(DF_rdChunk<<3)|(DF_rdPage<<1)|(DF_rdByte>>8);
  byte3=DF_rdByte;
  DF_error=0;
  DF_contRd=1;
  SPIPAUSE;
  SPISEND(BUF2TRANS);
  SPISEND(byte1);
  SPISEND(byte2);
  SPISEND(byte3);
  SPIWAITREADY(statreg); 
  SPIPAUSE;
  SPISEND(BUF2READ2);
  SPISEND(byte1);
  SPISEND(byte2);
  SPISEND(byte3);
  SPISEND(0x00);
}

void DF_finishRd(void) {
  DF_contRd=0;
  DF_error=0;
  CS=1;
}

void DF_puts(char* data, BYTE len) {
 char* p; 
 BYTE i;
 DF_error=0;
 if (!DF_contWr) {
    DF_startWr();
    DF_contWr=0;
 }                 
 p=data;
 for(i=0;i264){
   DF_wrByte-=264;
   DF_wrPage++;
   DF_wrPage&=0x03;
 }  
}   

void DF_putsf(char flash *data, BYTE len) {
 char flash * p;
 BYTE i;
 DF_error=0;
 if (!DF_contWr) {
	DF_startWr();
    DF_contWr=0;
 }
 p=data;
 for(i=0;i264){
   DF_wrByte-=264;
   DF_wrPage++;
   DF_wrPage&=0x03;
 }  
} 

void DF_gets(char* data, BYTE len) {
 char* p;
 BYTE i;
 DF_error=0;
 if (!DF_contRd) {
    DF_startRd();
    DF_contRd=0;
 }
 p=data;
 for(i=0;i264){
   DF_rdByte-=264;
   DF_rdPage++;
   DF_rdPage&=0x03;
 }  
}  

BYTE DF_ready(void) {
 BYTE statreg; 
 DF_error=0;
 SPIPAUSE;
 SPIGETSTAT(statreg);
 CS=1;
 return(statreg&0x80);
}
  

RS232/SLIP

These files define the functions that drive the RS232/SLIP interfaces for the router and host (host code not yet available). The transmitter and receiver use state machines to send and receive data. Acording to SLIP, a value of c0 is placed at the beginning and end of each packet. To transmit the value 0xc0, the word 0xdbdc is sent. To transmit the value 0xdb, the word 0xdbdd is sent. This allows packets to be identified with a minimum of processing.

Physical_init sets up the interfaces.

Physical_timer is called by the timer interrupt.

Physical_send puts a string on the interface, returning zero if this is not possible (i.e., there is already a write in progress. Link_done is called when this finishes.

Physical_end notifies the interface that the outgoing packet is ether starting or complete. According to SLIP specification, a value of 0xc0 is then transmitted.

Physical_set_ddr, Physical_set_char, Physical_clr_char, and Physical_read_char are all functions for manipulating single bits in I/O registers.

The interface calls link_notify when a byte has been received, link_end when 0xc0 is received, and link_done when a transmission is complete.

<physical.h>

#ifndef __PHYSICAL_H__ 
#define __PHYSICAL_H__  
      
#define PHYS_NUM_DEVS 4
#define RS232_BAUD 833 
#define RS232_PAUSETIME 160
#define RS232_SAMPLES 64
#define RS232_THRESHOLD 32

/* state machine variables */
#define SYNC 0 
#define START_BIT 1
#define SEND_BIT 2
#define STOP_BIT 3
#define PAUSE 4   

#define WAIT_STOP 0
#define WAIT_START 1
#define WAIT_GET 2
#define GET_BIT 3

// RS232 defines 
#define PAR_NONE  0    //parity types
#define PAR_ODD   1
#define PAR_EVEN  2
#define PAR_MARK  3
#define PAR_SPACE 4
   
#define SB_1  0        //stop bits
#define SB_15 1
#define SB_2  2

#define FLOW_NONE    0 //flow control
#define FLOW_XONXOFF 1
#define FLOW_HW      2

typedef struct _rs232devstate{
  BYTE sendState;
  BYTE * sendBuf, * sendPtr; 
  BYTE sendChar;
  BYTE sendCharsLeft;
  BYTE sendBitNum;
  BYTE sendSampleNum;
  BYTE sendPause;
  BYTE sendStart;
  BYTE sendESC, sendEND; // SLIP special characters
  BYTE recvState;
  BYTE recvInd;
  BYTE recvChar;
  BYTE recvBitNum; 
  BYTE recvSampleNum;
  BYTE recvOnes;  
  BYTE recvESC; // SLIP special characters
  WORD recvBytes;
} rs232devstate;

//external link interface          
extern void link_notify(BYTE recvByte, BYTE devnum);
extern void link_done(BYTE devnum);
extern void link_end(BYTE devnum);
     
void physical_set_ddr(BYTE devnum);
void physical_set_char(BYTE devnum);
void physical_clr_char(BYTE devnum);
BYTE physical_read_char(BYTE devnum);
 
void physical_init(void);
void physical_timer(void);

//interface functions
BYTE physical_put(void* in,  WORD bytes, BYTE devnum);
BYTE physical_end(BYTE devnum);

// test/set/clear macros
#asm  
.macro _set_dev_1 
  SBI 0x12,1
.endm
.macro _clr_dev_1
  CBI 0x12,1
.endm 
.macro _read_dev_1 
  CLR  R30
  SBIC 0x10,0
  LDI  R30,1
  MOV  @0,R30
.endm   
.macro _set_ddr_1
  CBI 0x11,0
  SBI 0x11,1
.endm
.macro _set_dev_2 
  SBI 0x12,3
.endm
.macro _clr_dev_2
  CBI 0x12,3
.endm
.macro _read_dev_2
  CLR  R30
  SBIC 0x10,2
  LDI  R30,1
  MOV  @0,R30
.endm
.macro _set_ddr_2
  CBI 0x11,2
  SBI 0x11,3
.endm
.macro _set_dev_3 
  SBI 0x12,5
.endm
.macro _clr_dev_3
  CBI 0x12,5
.endm
.macro _read_dev_3
  CLR  R30
  SBIC 0x10,4
  LDI  R30,1
  MOV  @0,R30
.endm
.macro _set_ddr_3
  CBI 0x11,4
  SBI 0x11,5
.endm
.macro _set_dev_4 
  SBI 0x12,7
.endm
.macro _clr_dev_4
  CBI 0x12,7
.endm
.macro _read_dev_4
  CLR  R30
  SBIC 0x10,6
  LDI  R30,1
  MOV  @0,R30
.endm
.macro _set_ddr_4
  CBI 0x11,6
  SBI 0x11,7
.endm
#endasm  

//physical state variables

rs232devstate DEVSTATE[PHYS_NUM_DEVS];

BYTE SLIP_ESC, SLIP_END;

WORD physical_send_len;
WORD physical_recv_len;
           
#endif //__PHYSICAL_H__
  

<physical.c>

#include 
#include "global.h"
#include "physical.h"
  
void physical_set_ddr(BYTE devnum) {
  switch(devnum) {
    case 0:
    #asm("_set_ddr_1");
    break;
    case 1:
    #asm("_set_ddr_2");
    break;
    case 2:
    #asm("_set_ddr_3");
    break;
    case 3:
    #asm("_set_ddr_4");
    break;	
  }
}
     
void physical_set_char(BYTE devnum) {
  switch(devnum) {
    case 0:
    #asm("_set_dev_1");
    break;
    case 1:
    #asm("_set_dev_2");
    break;
    case 2:
    #asm("_set_dev_3");
    break;
    case 3:
    #asm("_set_dev_4");
    break;	
  }
}

void physical_clr_char(BYTE devnum) {
  switch(devnum) {
    case 0:
    #asm("_clr_dev_1");
    break;
    case 1:
    #asm("_clr_dev_2");
    break;
    case 2:
    #asm("_clr_dev_3");
    break;
    case 3:
    #asm("_clr_dev_4");
    break;	
  }
} 

BYTE physical_read_char(BYTE devnum) {
  #asm("push r16");
  switch(devnum) {
    case 0:
    #asm("_read_dev_1 r16");
    break;
    case 1:
    #asm("_read_dev_2 r16");
    break;
    case 2:
    #asm("_read_dev_3 r16");
    break;
    case 3:
    #asm("_read_dev_4 r16");
    break;	
  }
  #asm   
  mov r30,r16
  pop r16
  #endasm
}
 

/* physical_init 
 * initialize the physical layer 
 * call this once
 */
void physical_init(void) { 
     BYTE i;
     rs232devstate* ds;
     
	OCR1A=RS232_BAUD;
	
  	TCCR1B=1; 		//full speed; NO clear-on-match
  	TCCR1A=0x00;	     //turn off pwm and oc lines
  	TIMSK=0x10;		//enable interrupt T1 cmp
      
     SLIP_ESC=0xdb;
     SLIP_END=0xc0;
         
     //direct all device states here
     for(i=0;isendState=SYNC;
  	       ds->sendBuf=0;
  		  ds->sendPtr=0; 
  		  ds->sendChar=0;
  		  ds->sendCharsLeft=0;
  		  ds->sendBitNum=0;
  		  ds->sendSampleNum=0;
  		  ds->sendPause=0;
  		  ds->sendStart=0; 
  		  ds->sendEND=0;
  		  ds->sendESC=0;
  		  ds->recvState=WAIT_STOP;
		  ds->recvInd=0;        
		  ds->recvChar=0;
		  ds->recvBitNum=0; 
		  ds->recvSampleNum=0;
		  ds->recvOnes=0;       
		  ds->recvBytes=0;
		  ds->recvESC=0;
      }     
      #asm("sei");
 }
        
/* physical_timer 
 * called by the timer ISR
 * used to send and receive bytes
 */
void physical_timer(void) { 
  BYTE i, readbit;
  rs232devstate* ds;
  OCR1A+=RS232_BAUD;
  for(i=0;isendState) {
	case SYNC:
	  if (ds->sendStart) {
	    if (ds->sendPause) {
	       ds->sendSampleNum=RS232_SAMPLES;
	       ds->sendPause=0;
	       ds->sendState=PAUSE;
	       physical_set_char(i);
	    }
	    else {
	       goto sendNextChar;
	    }
	  } 
	  break;
	case START_BIT:
	  if (ds->sendSampleNum==1) {
	    ds->sendSampleNum=RS232_SAMPLES;
	    ds->sendChar=*(ds->sendPtr++);
	    ds->sendBitNum=8;  
	    if (ds->sendChar==0xc0) {
	      if(ds->sendEND) {
	        ds->sendEND=0; // don't escape if literal END
	      } else {		// is required
	        ds->sendESC=!ds->sendESC;
	        if (ds->sendESC) {
	          ds->sendChar=0xdb;
	          ds->sendPtr--;
	          ds->sendCharsLeft++;
	        } else ds->sendChar=0xdc;
	      }
	    } else if (ds->sendChar==0xdb) {
	      ds->sendESC=!ds->sendESC;
	      if (ds->sendESC) {
	        ds->sendChar=0xdb;
	        ds->sendPtr--;
	        ds->sendCharsLeft++;
	      } else ds->sendChar=0xdd;
	    }
	    if (ds->sendChar&0x01) physical_set_char(i);
	    else physical_clr_char(i);
	    ds->sendState=SEND_BIT;
	  } 
	  else ds->sendSampleNum--;
	  break;
	case SEND_BIT:
	  if (ds->sendSampleNum==1) {
	    ds->sendSampleNum=RS232_SAMPLES;
	    ds->sendBitNum--;
	    ds->sendChar>>=1;
	    if (ds->sendBitNum==0) {
	    	physical_set_char(i);
	    	ds->sendState=STOP_BIT;
	    }
	    else {
	    	if (ds->sendChar&0x01) physical_set_char(i);
	    	else physical_clr_char(i);
	    }
	  } 
	  else ds->sendSampleNum--;
	  break;
	case STOP_BIT: 
	  if (ds->sendSampleNum==1) {
	  	ds->sendCharsLeft--;
	  	goto sendNextChar;
	  } 
	  else ds->sendSampleNum--;
	  break;
	case PAUSE:
	  if (ds->sendSampleNum==1) goto sendNextChar;
	  else ds->sendSampleNum--; 
	  break;
	default: 
   	  physical_set_char(i); 
	  ds->sendState=SYNC; 
	  break; 
sendNextChar:
      if (ds->sendCharsLeft) {
      	ds->sendSampleNum=RS232_SAMPLES;
      	physical_clr_char(i);
        ds->sendState=START_BIT;
      } else {
        ds->sendStart=0;
        physical_set_char(i); 
        link_done(i);  
        ds->sendState=SYNC;
      }
	}
  }
  for(i=0;irecvState) {
      case WAIT_STOP: 
        readbit=physical_read_char(i);
        if(readbit) ds->recvState=WAIT_START;
        break;
      case WAIT_START:
        readbit=physical_read_char(i);
        if(!readbit) {
          ds->recvSampleNum=RS232_SAMPLES;
          ds->recvState=WAIT_GET;
        }
        break;
      case WAIT_GET:         
        if(ds->recvSampleNum==1) {
          ds->recvOnes=0;
          ds->recvSampleNum=RS232_SAMPLES;
          ds->recvBitNum=8; 
          ds->recvState=GET_BIT;
        }
        else ds->recvSampleNum--;
        break;
      case GET_BIT:       
        readbit=physical_read_char(i);
        ds->recvOnes+=physical_read_char(i); 
        if(ds->recvSampleNum==1) {
           ds->recvSampleNum=RS232_SAMPLES;
           ds->recvBitNum--;
           ds->recvChar>>=1;
           ds->recvChar|=((ds->recvOnes>=RS232_THRESHOLD)?0x80:0x00);
           if(ds->recvBitNum==0) {
             if (ds->recvESC) {
             	ds->recvESC=0;
               switch(ds->recvChar) {
                 case 0xdc: link_notify(0xc0,i); break;
                 case 0xdd: link_notify(0xdb,i); break;
                 default: link_notify(ds->recvChar,i); break;
               }              
             } else if (ds->recvChar==0xc0) {
               link_end(i);
             } else if (ds->recvChar==0xdb) {
               ds->recvESC=1;
             } else link_notify(ds->recvChar,i);
             ds->recvState=WAIT_STOP;
           }    
           ds->recvOnes=0;
        } 
        else ds->recvSampleNum--;
        break;
      default:
        ds->recvState=WAIT_STOP; 
        break;
    }
  }
}
                
/* physical_put 
 * starts a send on the device
 * returns 1 if successful
 * returns 0 if send already in progress
 */
BYTE physical_put(void* in,  WORD bytes, BYTE devnum){
  rs232devstate *ds;
  ds = &DEVSTATE[devnum];
  if (ds->sendCharsLeft!=0) return 0;
  ds->sendBuf=in;
  ds->sendPtr=in;
  ds->sendStart=1;
  ds->sendPause=1; 
  ds->sendEND=0;
  ds->sendCharsLeft=bytes;    
  return 1;
}          

/* physical_end 
 * sends a SLIP "END" (0xc0)
 * returns 1 if successful
 * returns 0 if send already in progress
 */
BYTE physical_end(BYTE devnum){     
  rs232devstate *ds;          
  ds = &DEVSTATE[devnum];                             
  if (ds->sendCharsLeft!=0) return 0;
  ds->sendBuf=&SLIP_END;
  ds->sendPtr=&SLIP_END;
  ds->sendStart=1;
  ds->sendPause=1;
  ds->sendEND=1;  // flag so the SM allows END character
  ds->sendCharsLeft=1;    
  return 1;
}

interrupt [TIM1_COMPA] void t1_cmpA(void) {
  physical_timer();
}
  

<physical-h.h>

The host physical/link layer is not written yet.

<physical-h.c>

The host physical/link layer is not written yet.

IP Transmitter/Receiver

The functions link_notify, link_end, and link_done, called by the physical layer, are defined here. They serve to drive two state machines for receiving and transmitting IP packets over SLIP. The receiver first waits for an end message. Then, it decodes the IP header without the options. It then sets some variables based on the header, does error-checking and aborts the receive, calling the error event handler and finish_recv event handler if necessary. After receiving the header, it calls the start_recv event handler, then begins receiving any options present. At this point it begins forwarding data, using the recv event handler, every 33 bytes. After all options are received, the checksum is verified and the receive is aborted if the check fails. After this the data portion of the packet is received until the number of bytes received equals the payload length in the IP header. Data is forwarded every 33 bytes. If link_end() is called before the packet completes, the receive is aborted. When the packet completes, it forwards the remaining bytes and waits for link_end(), then calls finish_send(). It then repeats the process.

The transmitter first checks the queue. If it is empty, it clears a busy flag, permitting a direct send. For a direct send, it then waits for a start_send omessage, after which it sets the busy flag and calls physical_end and waits for link_done to be called. It then waits for a send or finish_send message. On a send message, it swaps its secondary and primary buffers, calls physical_send for the primary buffer, and waits for link_done(). This is to enable other functions to edit the secondary buffer, then post a send message. It then returns to its waiting state for send or finish_send. On finish_send, it calls physical_end() and waits for link_done(), then returns to its starting state.

If the queue is NOT empty, it dequeues the block number in the dataflash and length (in 32-bit words) of the packet. Two queues exist for this purpose since each requires a byte. It then calls physical_end(), waits for link_done(), then loads 33 bytes at a time from the dataflash into its primary buffer, then calls physical_send() on the buffer and waits for link_done(). It repeats this until the correct number of bytes has been transmitted, then calls physical_end(), waits for link_done(), and returns to its start state.

<link.h>

The IP transmitter code has not been verified to work.

#ifndef __LINK_H__
#define __LINK_H__ 
  
#include "queue.c"  
  
#define LINK_NUM_INTS 4
#define LINK_MTU 1006
#define LINK_MAX_PACKETS 64
 
//sending state machine states:
//all odd numbers, one number less is waiting state
//  for that state             

#define IP_SEND_DONE      1
#define IP_SEND_IDLE      3
#define IP_SEND_WAIT_SEND 5
#define IP_SEND_CHECK_LEN 7
#define IP_SEND_SEND_END  9
    
//sending state machine messages:

#define IP_SEND_MSG_NONE       0
#define IP_SEND_MSG_START_SEND 1
#define IP_SEND_MSG_END_SEND   2
#define IP_SEND_MSG_SEND_DATA  3
   
//receiving state machine states:

#define IP_RECV_IDLE     0
#define IP_RECV_WAIT     1
#define IP_RECV_HEAD1    2
#define IP_RECV_HEAD2    3
#define IP_RECV_OPT1     4
#define IP_RECV_OPT2     5
#define IP_RECV_BODY     6
#define IP_RECV_WAIT_END 7  
 
//receiving state machine messages:

#define IP_RECV_MSG_NONE 0
#define IP_RECV_MSG_END  1
#define IP_RECV_MSG_RECV 2
    
//send method:

#define IP_SEND_NONE   0
#define IP_SEND_DIRECT 1
#define IP_SEND_MEM    2

BYTE link_scratch_buffer[33][4][LINK_NUM_INTS];
BYTE link_queue_buffer[LINK_MAX_PACKETS][2][LINK_NUM_INTS];

typedef struct {
  BYTE *priSendBuf, *secSendBuf;
  BYTE *priRecvBuf, *secRecvBuf;
  BYTE *tmpBuf;
  BYTE sendState, recvState;       
  BYTE recvPage, sendPage;
  BYTE recvQuad, sendQuad;
  WORD recvByte, sendByte;
  WORD recvLength, sendLength;
  BYTE recvMsg, sendMsg;
  BYTE recvParam, sendParam;
  
  BYTE sendBusy;
  queue pageQueue, lengthQueue;
  
  BYTE recvIndex, recvChar; 
  BYTE recvDestInt,
       recvEndAllowed, 
       recvSendMethod,
       recvError;
  struct iphdr* recvHeader;    
  
  WORD IPchecksum;         
  WORD IPtotalLen;
  BYTE IPheadLen; 
  DWORD IPdestAddr;
} link_interface;

link_interface INTERFACES[LINK_NUM_INTS];
 
extern void IP_error(link_interface* li);
extern void IP_start_recv(link_interface* li);
extern void IP_recv(link_interface* li);
extern void IP_finish_recv(link_interface* li); 

extern BYTE IP_start_send(link_interface* li, BYTE* page); 
extern BYTE* IP_send(
  link_interface* li, 
  BYTE send_method, 
  BYTE* data, 
  BYTE length,
  BYTE page, 
  BYTE quad, 
  WORD byte);
extern void IP_finish_send(
  link_interface* li, 
  BYTE send_method, 
  BYTE error, 
  BYTE page, 
  BYTE totalLen);

extern BYTE IP_lookup(DWORD ipaddr);

//state machine iterations

void link_send_state(link_interface* li, BYTE intnum);
void link_recv_state(link_interface* li, BYTE intnum); 
    
//called by physical layer

void link_notify(BYTE recvByte, BYTE devnum);
void link_done(BYTE devnum);
void link_end(BYTE devnum);
                          
//called by MCU

void link_init();
void link_main();   
                   
#endif //__LINK_H__
  

<link.c>

#include "global.h"
#include "link.h" 
#include "dflash.c"
#include "physical.c"
  
void link_notify(BYTE recvByte, BYTE devnum) {
  link_interface* li;
  li=&INTERFACES[devnum]; 
  if(li->recvMsg!=IP_RECV_MSG_NONE) {
    IP_error(li);
    li->recvMsg=IP_RECV_MSG_END;
  }
  li->recvParam=recvByte;
  li->recvMsg=IP_RECV_MSG_RECV;
}

void link_done(BYTE devnum) {
   link_interface* li;
   li=&INTERFACES[devnum];
   if (!(0x01&(li->sendState))) {
     li->sendState++;
   }
}
                 
void link_end(BYTE devnum) {
   link_interface* li;   
   li=&INTERFACES[devnum]; 
   li->recvMsg=IP_RECV_MSG_END;
}

void link_send_state(link_interface* li, BYTE intnum) {
     switch(li->sendState) {
       case IP_SEND_DONE: 
         if (li->pageQueue.length==0) {   
           li->sendState=IP_SEND_WAIT_SEND-1; 
           li->sendBusy=0;
         } else {
           li->sendState=IP_SEND_CHECK_LEN-1; 
           physical_end(intnum);
           li->sendPage=queue_dequeue(&li->pageQueue);
           li->sendLength=queue_dequeue(&li->lengthQueue);  
           li->sendQuad=0;
           li->sendByte=0;
           DF_rdCue(li->sendPage,li->sendQuad,li->sendByte);
           DF_gets(li->secSendBuf,33);                      
           li->sendByte+=33;
           if(li->sendByte==264) {
             li->sendByte=0;
             li->sendQuad++;
             li->sendQuad&=0x3;
           }  
         }
         return;
       case IP_SEND_IDLE:
         if (li->pageQueue.length!=0) {
           li->sendState=IP_SEND_DONE;
           li->sendBusy=1;
         } else if(li->sendMsg==IP_SEND_MSG_START_SEND) {
           li->sendMsg=IP_SEND_MSG_NONE; 
           li->sendState=IP_SEND_WAIT_SEND-1;
           physical_end(intnum);
           li->sendBusy=1;
         } 
         return;
       case IP_SEND_WAIT_SEND:
         if(li->sendMsg==IP_SEND_MSG_SEND_DATA) {
           li->sendMsg=IP_SEND_MSG_NONE; 
		 li->sendState=IP_SEND_WAIT_SEND-1;
           li->sendLength=li->sendParam;
           li->tmpBuf=li->priSendBuf;
           li->priSendBuf=li->secSendBuf;
           li->secSendBuf=li->tmpBuf;
           physical_put(li->priSendBuf,li->sendLength,intnum);
         } else if(li->sendMsg==IP_SEND_MSG_END_SEND) {
           li->sendState=IP_SEND_DONE-1; 
           physical_end(intnum);
         }
         return;
       case IP_SEND_CHECK_LEN:
         if(li->sendLength<=33) {
           li->sendState=IP_SEND_SEND_END-1;
           li->tmpBuf=li->priSendBuf;
           li->priSendBuf=li->secSendBuf;
           li->secSendBuf=li->tmpBuf;
           physical_put(li->priSendBuf,li->sendLength,intnum);
           li->sendLength=0;   
         } else {
           li->sendState=IP_SEND_CHECK_LEN-1;
           li->tmpBuf=li->priSendBuf;
           li->priSendBuf=li->secSendBuf;
           li->secSendBuf=li->tmpBuf;
           physical_put(li->priSendBuf,33,intnum);
           DF_rdCue(li->sendPage,li->sendQuad,li->sendByte);
           DF_gets(li->secSendBuf,33);                      
           li->sendByte+=33;
           if(li->sendByte==264) {
             li->sendByte=0;
             li->sendQuad++;
             li->sendQuad&=0x3;
           }
           li->sendLength-=33;            
         }
         return;
       case IP_SEND_SEND_END:     
         li->sendState=IP_SEND_DONE-1;
         physical_end(intnum);
         DF_dealloc(li->sendPage); 
         return;
     }
}

void link_recv_state(link_interface* li, BYTE intnum) {
  if (li->recvMsg==IP_RECV_MSG_END&&li->recvEndAllowed==0){
    li->recvMsg=IP_RECV_MSG_NONE; 
    li->recvState=IP_RECV_IDLE;
    IP_error(li);
    li->recvEndAllowed=1;
  }
  switch(li->recvState) {
  case IP_RECV_IDLE:      
    if(li->recvMsg==IP_RECV_MSG_END){
      li->recvMsg=IP_RECV_MSG_NONE; 
      li->recvState=IP_RECV_WAIT;
      li->recvIndex=0;
      li->recvLength=0;
      li->IPchecksum=0;
      li->recvHeader=(struct iphdr*) li->priRecvBuf;
    }
    return;
  case IP_RECV_WAIT:
    if(li->recvMsg==IP_RECV_MSG_RECV) {
      li->recvState=IP_RECV_HEAD1;
      li->priRecvBuf[li->recvIndex++]=li->recvParam;
      li->IPchecksum+=(((WORD)(li->recvParam))<<8);
      li->recvLength++;
      li->recvMsg=IP_RECV_MSG_NONE; 
      li->recvEndAllowed=0;
      li->IPheadLen=(((BYTE)(li->recvHeader->ihlver&0x0f))<<2);      
    }
    return;
  case IP_RECV_HEAD1:
    if(li->recvMsg==IP_RECV_MSG_RECV) {
      li->priRecvBuf[li->recvIndex++]=li->recvParam;
      li->IPchecksum+=((WORD)(li->recvParam));
      li->recvLength++;
      li->recvMsg=IP_RECV_MSG_NONE; 
      if(li->recvLength!=20) {
        li->recvState=IP_RECV_HEAD2;
        return;
      } else if ((li->recvHeader->ihlver&0x0f)<5 ||
                 (li->recvHeader->ihlver>>4)!=4 ||
                 byteswap16(li->recvHeader->tot_len)>1006) { 
        goto __droppacket;
      } else {
        li->IPtotalLen=byteswap16(li->recvHeader->tot_len);
        li->IPdestAddr=byteswap32(li->recvHeader->daddr);
        li->recvDestInt=IP_lookup(li->IPdestAddr);
        goto __checkoptions;
      }
    }
    return;
  case IP_RECV_HEAD2:    
    if(li->recvMsg==IP_RECV_MSG_RECV) {
      li->recvState=IP_RECV_HEAD1;
      li->priRecvBuf[li->recvIndex++]=li->recvParam;
      li->IPchecksum+=(((WORD)(li->recvParam))<<8);
      li->recvLength++; 
      li->recvMsg=IP_RECV_MSG_NONE; 
      return;
    }
    return;
  case IP_RECV_OPT1: 
    if(li->recvMsg==IP_RECV_MSG_RECV) {  
      li->priRecvBuf[li->recvIndex++]=li->recvParam;
      li->IPchecksum+=((WORD)(li->recvParam));
      li->recvLength++;
      li->recvMsg=IP_RECV_MSG_NONE; 
      goto __checkoptions;
    }
    return;
  case IP_RECV_OPT2:        
    if(li->recvMsg==IP_RECV_MSG_RECV) {
      li->recvState=IP_RECV_OPT1;
      li->priRecvBuf[li->recvIndex++]=li->recvParam;
      li->IPchecksum+=(((WORD)(li->recvParam))<<8);
      li->recvLength++;
      li->recvMsg=IP_RECV_MSG_NONE; 
      if(li->recvIndex==33) {
        IP_recv(li);
        li->recvIndex=0;
      } 
      return;                
    }
    return;
  case IP_RECV_BODY:    
    if(li->recvMsg==IP_RECV_MSG_RECV) {
      li->priRecvBuf[li->recvIndex++]=li->recvParam;
      li->recvLength++;
      li->recvMsg=IP_RECV_MSG_NONE; 
      if(li->recvLength==li->IPtotalLen) {
        li->recvEndAllowed=1;
        IP_recv(li);
        li->recvState=IP_RECV_WAIT_END;
      }
      if(li->recvIndex==33) {
        IP_recv(li);
        li->recvIndex=0;
      } 
      return;                
    }
    return;
  case IP_RECV_WAIT_END:
    if(li->recvMsg==IP_RECV_MSG_END) {
      li->recvState=IP_RECV_IDLE;    
      IP_finish_recv(li);  
      li->recvError=0;
      li->recvSendMethod=IP_SEND_NONE;
      return;
    }
    return;
  __checkoptions:
    if(li->recvLength!=li->IPheadLen){
      li->recvState=IP_RECV_OPT2;
      return;
    } else if(li->IPchecksum!=0xFFFF) { 
      goto __droppacket;
    } else {
      li->recvState=IP_RECV_BODY;
      return;
    }
    return;
  __droppacket:
    IP_error(li);
    li->recvEndAllowed=1;
    li->recvState=IP_RECV_WAIT_END;
    return;
  }
}

void link_main() {  
   link_interface* li;
   BYTE intnum;
   
   for(intnum=0;intnumrecvMsg=IP_SEND_MSG_NONE;
   }
}
            
void link_init() { 
   link_interface* li;
   BYTE intnum; 
   DF_init();   
   for(intnum=0;intnumpriSendBuf=link_scratch_buffer[intnum][0];
     li->secSendBuf=link_scratch_buffer[intnum][1];
     li->priRecvBuf=link_scratch_buffer[intnum][2];
     li->secRecvBuf=link_scratch_buffer[intnum][3];
     
     li->pageQueue.head=link_queue_buffer[intnum][0];
     li->lengthQueue.head=link_queue_buffer[intnum][1];
     li->pageQueue.length=0;
     li->lengthQueue.length=0;
     li->pageQueue.maxsize=LINK_MAX_PACKETS;
     li->lengthQueue.maxsize=LINK_MAX_PACKETS; 
     
     li->sendState=IP_SEND_DONE;
     li->sendBusy=1;   
     li->sendMsg=IP_SEND_MSG_NONE;
     
     li->recvState=IP_RECV_IDLE;
     li->recvEndAllowed=1;
     li->recvError=0;
     li->recvMsg=IP_RECV_MSG_NONE;
   }   
}
                           
  

IP Handlers/Drivers

This code has not been verified to work. Briefly, however, how it is *supposed* to function is that the receiver event handlers call transmitter driver functions for the router and the IP_lookup() function, thus forwarding any received data.

<ip-r.c>

#include 
#include "global.h"
#include "led.h"
#include "link.c"
      
void IP_error(link_interface* li) {
  // drop interface's current packet
  led_red(); 
  IP_finish_send(&INTERFACES[li->recvDestInt],li->recvSendMethod,
                 1,li->recvPage,0);
  li->recvError=1;
  li->recvSendMethod=IP_SEND_NONE;
} 

void IP_start_recv(link_interface* li) {
  led_yellow();
  if(li->recvSendMethod==IP_SEND_NONE) {
    li->recvSendMethod=
      IP_start_send(&INTERFACES[li->recvDestInt],
        &(li->recvPage));
    li->recvQuad=0;
    li->recvByte=0;
  }
}

void IP_recv(link_interface* li) {
  li->secRecvBuf=IP_send(
    &INTERFACES[li->recvDestInt],
    li->recvSendMethod, 
    li->secRecvBuf,
    li->recvIndex,
    li->recvPage,
    li->recvQuad,
    li->recvByte); 
  li->recvByte+=li->recvIndex;
  if(li->recvByte>=264) {
    li->recvByte=0;
    li->recvQuad++;
    li->recvQuad&=0x03;
  }  
  li->recvIndex=0;
}    

void IP_finish_recv(link_interface* li) {
  if (li->recvError) return;
  led_green(); 
  IP_finish_send(
    &INTERFACES[li->recvDestInt],  
    li->recvSendMethod,
    0,
    li->recvPage,
    li->recvLength);
}
  
BYTE IP_start_send(link_interface* li, BYTE* page) { 
   if(li->sendBusy==1) {
     *page=DF_alloc();
     return IP_SEND_MEM;
   }
   else {  
     li->sendBusy=1;
     li->sendMsg=IP_SEND_MSG_START_SEND;  
     return IP_SEND_DIRECT;
   } 
}

BYTE* IP_send(link_interface* li, 
               BYTE send_method, 
               BYTE* data, 
               BYTE length,
               BYTE page, 
               BYTE quad, 
               WORD byte) 
{ 
  if(send_method==IP_SEND_DIRECT) {   
    li->tmpBuf=li->secSendBuf;
    li->secSendBuf=data;
    li->sendParam=length;
    li->sendMsg=IP_SEND_MSG_SEND_DATA;
    return li->tmpBuf;
  } else {
    DF_wrCue(page,quad,byte);
    DF_puts(data,length);
    return data;
  }
}

void IP_finish_send(
  link_interface* li, 
  BYTE send_method, 
  BYTE error, 
  BYTE page, 
  BYTE totalLen) 
{
  if(send_method==IP_SEND_DIRECT) {  
    li->sendMsg=IP_SEND_MSG_END_SEND; 
  } else {
    if (error) {
      DF_dealloc(page);
      return;
    }
    queue_enqueue(&li->pageQueue,page);
    queue_enqueue(&li->lengthQueue,totalLen);
  } 
}

BYTE IP_lookup(DWORD ipaddr) {
  // return interface number based on IP address
  return 0;
}
   
void main() {  
  BYTE statreg;
  physical_init();
  DDRC=0x07; //setup the LEDs  
  PORTC=0x07; //turn them off;
  link_init(); 
  while(1) {
     link_main();
  }
}
  

<ip-h.c>

The host IP handlers and drivers are not written yet.

TCP

<tcp.h>

TCP is not written yet.

<tcp.c>

TCP is not written yet.

UDP

<udp.h>

UDP is not written yet.

<udp.c>

UDP is not written yet.

Application

<main.c>

The end host application is not written yet.