//----------------------------------------------------------------------------
// RPC232                                                     19960218 CHG
//
// Function:
// 
// RPC232 transfers data from one modules RS232 port to another modules RS232
// port. A simple RTS/CTS protocol is implemented. It works as follows:
//
//   A HOST is only allowed to transmit data to it's RPC232 module as long as 
// the RTS signal from the module is active. If the RTS signal from the RPC232 
// module goes inactive, it's a signal to the HOST of that module to stop 
// transmitting as the modules receive buffer is full. This 'buffer full'
// indication, will be set when the RPC232 module has received 20 bytes from 
// its HOST. These 20 bytes will then be sent to the other RPC232 module, and 
// the RTS signal will then again be asserted, and the HOST is allowed to send 
// more data. If the HOST don't transmit 20 bytes at a time but maybe only
// 10 bytes, the RPC232 module will timeout after the 10. byte in approx. 
// 20 mSec, and transfer the 10 bytes to the other RPC232 module. 
// 
//   The CTS signal from the HOST to its RPC232 module works in a similar 
// fashion, if the HOST can't receive the data from its RPC232 module it can 
// deassert the CTS signal to the module. When the HOST is ready to receive 
// the rest of the data from the RPC232 module, it simply asserts the CTS 
// signal again to the module. 
//  
// The effect of this RTS/CTS protocol is that the sustained transfer rate
// of the radio link is not 9600 baud as is the speed between one RPC232
// module and its HOST. Instead the 'effective' baudrate is around 6300 baud.
// Seen from both HOST's, its 9600 baud, with data comming in small (20 bytes)
// packets.
//    Ŀ
//                                 .
//                                 .
//                                 .
//                                 .
//                                 .
//                                 .
//    Ŀ           .
//                     .
//    
//     0V +V C  R  R  T
//     \   / T  T  x  x
//      \ /  S  S  D  D
//       |   |  |  |  |To HOST
//    9-24 V |  |  |From HOST
//           |  |To HOST
//           |From HOST
//
//----------------------------------------------------------------------------
#include <reg2051.h>
#include <ctype.h>
#include <stdio.h>

//----------------------------------------------------------------------------
// Constants...
//----------------------------------------------------------------------------
#define TRUE     1 
#define FALSE    0 
#define RxSize  27
 
// Various states... 
#define IDLE     0 
#define FROMHOST 1 
 
//----------------------------------------------------------------------------
// I/O definitions...
//----------------------------------------------------------------------------
#define RPC_Reset P16  // Reset* RPC module
#define RPC_RxA   P15  // RxA* to RPC
#define RPC_RxR   P32  // RxR* from RPC (Used only as INT0* input)
#define RPC_TxA   P33  // TxA* from RPC
#define RPC_TxR   P14  // TxR to RPC
#define RPC_D3    P13  // D3/D7 to/from RPC
#define RPC_D2    P12  // D2/D6 to/from RPC
#define RPC_D1    P11  // D1/D5 to/from RPC
#define RPC_D0    P10  // D0/D4 to/from RPC
 
#define RTS       P34  // RTS to HOST 
#define CTS       P35  // CTS from HOST (Not used) 
#define ASSERT      0  // Active state of RTS/CTS
#define DEASSERT    1  // InActive state of RTS/CTS

//----------------------------------------------------------------------------
// Variables...
//----------------------------------------------------------------------------
unsigned char RxBuffer[RxSize]; // Frame received from either HACBUS or RPC
unsigned char Index;            // Index into RxBuffer
unsigned char State=IDLE;       // State we are in
unsigned char HOSTTimeOut=0;    // Timeout between chars from HOST
 
//----------------------------------------------------------------------------
// Wait for a specific time in 10 mSec
// Based on a 11.059 MHz crystal
//----------------------------------------------------------------------------
void Delay(unsigned char t) {
  unsigned int i;
  if (t==0) return;
  while (t--) for(i=0;i<774; i++);
}

//----------------------------------------------------------------------------
// Timer 0 interrupt.
// Timer preloads:
//     0xDC00 : 10.0 mSec timeout
//----------------------------------------------------------------------------
void Timer0(void) interrupt 1 using 2 {
  // Load Timer again...
  TH0  = 0xDC; // set timer period for timer 0
  TL0  = 0x00; // to 10.0 mSec @ 11.0592 MHz clock
  if (HOSTTimeOut) HOSTTimeOut--;
}

//----------------------------------------------------------------------------
// RxR issued from RPC, ie it has some data for us...
//---------------------------------------------------------------------------- 
void Int0(void) interrupt 0 using 1 {
  static unsigned char Count=0;
  unsigned char RPCRxChar;
 
  RTS=DEASSERT; // Tell HOST to be silent
 
  RPC_D0=RPC_D1=RPC_D2=RPC_D3=1; // Configure port-pins as inputs...
  RPC_RxA=0; // Acknowledge to RPC module
  while (RPC_RxR==0); // Wait for stable data from RPC
  // Get LSB
  RPCRxChar=(RPC_D0 ? 1:0) + (RPC_D1 ? 2:0) + (RPC_D2 ? 4:0) + (RPC_D3 ? 8:0);
  RPC_RxA=1; // Ready for next nibble

  while (RPC_RxR==1); // Wait for MSB 
  RPC_RxA=0; // Acknowledge to RPC module
  while (RPC_RxR==0); // Wait for stable data from RPC
  // Get MSB
  RPCRxChar+=(RPC_D0 ? 0x10:0) + (RPC_D1 ? 0x20:0) + (RPC_D2 ? 0x40:0) + (RPC_D3 ? 0x80:0);
  IE0=0;     // Clear pending int's from the MSB part RPC_RxR signal
  RPC_RxA=1; // Ready for next byte
 
  // If we already got the length byte, (ie this is a data byte)
  // go send it to the HOST
  if (Count) {
    while (TI==0);
    TI=0;
    // Wait for HOST to accept new data
    while (CTS==DEASSERT);
    SBUF=RPCRxChar;
    Count--;
    // Check if last byte received
    if (Count==0)
      RTS=ASSERT; // Allow HOST to send data to us again
  } else {
    // This is the first byte we are receiving, so it must be the length
    Count=RPCRxChar-1;
  }
}

//----------------------------------------------------------------------------
// Wait for RPC transmitter empty, and send one byte.
// Returns: Data sent 
//----------------------------------------------------------------------------
static unsigned char SendRPC(unsigned char Data) {
  // Send the LSB part...
  RPC_TxR=0; // Initiate transfer to RPC module
  while (RPC_TxA==1); // Wait for RPC to signal it's ready for transfer
  // Put out LSB data
  RPC_D0=(Data & 0x01) ? 1:0; RPC_D1=(Data & 0x02) ? 1:0;
  RPC_D2=(Data & 0x04) ? 1:0; RPC_D3=(Data & 0x08) ? 1:0;
  RPC_TxR=1; // Tell RPC that data is present
  while (RPC_TxA==0); // Wait until RPC has accepted the data
  // Send the MSB part...
  RPC_TxR=0; // Initiate transfer to RPC module
  while (RPC_TxA==1); // Wait for RPC to signal it's ready for transfer
  // Put out MSB data
  RPC_D0=(Data & 0x10) ? 1:0; RPC_D1=(Data & 0x20) ? 1:0;
  RPC_D2=(Data & 0x40) ? 1:0; RPC_D3=(Data & 0x80) ? 1:0;
  RPC_TxR=1; // Tell RPC that data is present
  while (RPC_TxA==0); // Wait until RPC has accepted the data
  RPC_D0=RPC_D1=RPC_D2=RPC_D3=1; // Turn bus into input state...
  // And return the same data (for checksum etc)
  return Data;
}


//----------------------------------------------------------------------------
// Main part
//----------------------------------------------------------------------------
void main() {
  unsigned char Length;
 
  RTS=DEASSERT; // Tell host to be quiet
  // Reset RPC module
  Delay(50);
  RPC_Reset=0;
  Delay(3);
  RPC_Reset=1;
  Delay(50);


 //------------------------------
 // Configure onchip resources...
 //------------------------------

  SCON  = 0x50;                  // SCON: mode 1, 8-bit UART, enable rcvr
  TMOD |= 0x20; //FF = 28.8KB    // TMOD: timer 1, mode 2, 8-bit reload
  TH1   = 0xFD; //FD = 9.6KB     // TH1:  reload value for baudrate generator
  PCON  = 0x00; // ==> double    // PCON: Double baudrate if bit 7 is set
  TR1   = 1;                     // TR1:  timer 1 run
 
  TI    = 1;                     // TI:   set TI to send first char of UART
 
  TMOD |= 0x01;                  // TMOD: timer 0, mode 1, 16 timer
  TH0   = 0xDC;                  // set timer period for timer 0
  TL0   = 0x00;                  // to 10.0 mSec @ 11.0592 MHz clock
  ET0   = 1;                     // Enable Timer 0 interrupts
 
  EX0   = 1;                     // External interrupt 0 enable
  IT0   = 1;                     // External interrupt 0 Edge sensitive
 
  EA    = 1;                     // Global int enable
 
  RTS=ASSERT; // Tell host it's ok to send data
  //----------------------------------------------------------------------------
  // Forever
  //----------------------------------------------------------------------------
  while(1) {
    switch (State) {  
      //----------------------------------------------
      // Look for data from either partner RPC
      // or from HOST, and switch to correct state
      //----------------------------------------------
      case IDLE:
        // HOST is allowed to send data to us...
        RTS=ASSERT;
        //----------------------------------------------
        // Check if HOST has some data to send to us...
        //----------------------------------------------
        if (RI==1) {
          State=FROMHOST;
        }
        break;
      //----------------------------------------------
      // Receive data from HOST
      //----------------------------------------------
      case FROMHOST:
        Index=0;
        // We allow max 25 bytes per transmission...
        while (Index<25) {
          // Check if HOST is about to fill up the RxBuffer, 
          // if so tell the HOST to be silent
          if (Index>20) 
            RTS=DEASSERT;
          else
            RTS=ASSERT;
          // Set timeout to 10 characters time (1 char @ 9600 baud = 1.041 mSec)
          HOSTTimeOut=3;
          TR0   = 1; // Start timer 0
          // Look for a character from HOST, in maximum 500 mSec
          while ((RI==0) && HOSTTimeOut);
          if (RI==1) {
            RxBuffer[Index++]=SBUF;
            RI=0;
          }
          // If it was a timeout, stop receiving from HOST
          if (HOSTTimeOut==0) break;
        }
        TR0   = 0; // Stop timer 0
        RTS=DEASSERT; // Tell HOST to be silent
        // Send the received data to the partner RPC
        Length=Index+1;
        Index=0;
        SendRPC(Length); // Send length of data...
        while (--Length)
          SendRPC(RxBuffer[Index++]); // and send each data byte...
        State=IDLE;
        break;
    }
  }
}
