//----------------------------------------------------------------------------
// DTMF to Relay decoder Vers. 2.0                            19950722 KIA/CHG
// Decodes DTMF tones, and toggles 1 of 16 relays according to the DTMF code
// received. The state of the 16 relays (on/off) is sent as 16 bytes, where a
// active port will be sent as a '1' (0x31h) and an inactive as a '0' (0x30h).
// The 16 bytes of status will be enclosed by STX, Checksum and ETX. 
// STX/ETX is defined by SW1-1 (see below). 
// 
//    *                                                   *   *
//    STX        <Status for each of the 16 relays>      Sum ETX
//    кФФТФФТФФТФФТФФТФФТФФТФФТФФТФФТФФТФФТФФТФФТФФТФФТФФТФФТФФП
//  ФФД02ГP1ГP2ГP3ГP4ГP5ГP6ГP7ГP8ГP9ГP0ГP*ГP#ГPAГPBГPCГPDГCSГ03УФФ
//    РФФСФФСФФСФФСФФСФФСФФСФФСФФСФФСФФСФФСФФСФФСФФСФФСФФСФФСФФй
// *: 
// The format is also dependent of the setting of SW1-1 ! (se below)
// 
// The data will be sent as follows: 1 startbit, no parity and 1 stopbit.
// The Checksum, CS, is the 8-bit sum of all bytes from STX to PD, both 
// inclusive. (NB: The checksum can be 03 (ETX), so use length AND checksum
// of the datagram to verify that it's correct received)
//
// Configuration switches:
//      SW1-1: Use CR as STX and LF as ETX, and omit the checksum 
//      SW1-2: Sends DTMF code received as simple ASCII on RS232..
//             And receives DTMF Ascii codes on RS232 and outputs them as
//             DTMF tones
//      SW1-3,SW1-4 selects the transmission baudrate as follows:
//        Baud: 1200 2400 4800 9600
//      SW1-3:    0    1    0    1
//      SW1-4:    0    0    1    1
//        Where a '0' represents the "OFF" position of the dipswitch and a
//        '1' represents the "ON" position of the dipswitch.
//----------------------------------------------------------------------------
#pragma OT(6, SIZE)
#pragma ROM(SMALL) 

#include <reg751.h>
#include <stdio.h>
 
//----------------------------------------------------------------------------
// Constants...
//----------------------------------------------------------------------------                     
#define TRUE  1
#define FALSE 0                
#define CR    0x0D
#define LF    0x0A
#define STX   0x02 
#define ETX   0x03

//----------------------------------------------------------------------------
// I/O definitions...
//---------------------------------------------------------------------------- 
// RX part of DTMF tranceiver... 
#define DTMFD0  P16    // Data bit 0 from 75T2090 DTMF decoder
#define DTMFD1  P17    // Data bit 1 from 75T2090 DTMF decoder
#define DTMFD2  P37    // Data bit 2 from 75T2090 DTMF decoder
#define DTMFD3  P36    // Data bit 3 from 75T2090 DTMF decoder
#define DTMFDV  P35    // Data available from 75T2090 DTMF decoder

// TX part of DTMF tranceiver, driven by UCN5818, bit numbers
#define DTMFD4  12     // Data bit 0 to 75T2090 DTMF decoder
#define DTMFD5  13     // Data bit 1 to 75T2090 DTMF decoder
#define DTMFD6  14     // Data bit 2 to 75T2090 DTMF decoder
#define DTMFD7  15     // Data bit 3 to 75T2090 DTMF decoder
#define DTMFLATCH 11   // Latch signal to 75T2090 DTMF decoder
#define DTMFRESET 10   // Reset signal to 75T2090 DTMF decoder
 
#define OutClk  P10    // Clock to UCN5818 output driver
#define OutStr  P11    // Strobe to UCN5818 output driver
#define OutData P12    // Data to UCN5818 output driver
#define OutBlk  P13    // Blanking to UCN5818 output driver

#define SWReset P01    // Reset all outputs
#define SW11    P33    // Dipswitch 1
#define SW12    P32    // Dipswitch 1
#define SW13    P31    // Dipswitch 1
#define SW14    P30    // Dipswitch 1
#define SwOn    0      // Dipswitch is in "on" position
#define SwOff   1      // Dipswitch is in "off" position

#define LED     P02    // LED
 
#define IR      P00    // IR Sensor


//----------------------------------------------------------------------------
// Variables...
//----------------------------------------------------------------------------
// UCN5818 driver specific... 
#define  BitsOut       32              // Max number of outputs 
                                       // (number of UCN5818's * 32)
unsigned char Output[BitsOut / 8];     // number of bytes to hold these outputs

// RS232 Specific variables...
#define RS232TxD P34           // RS232 transmit data
#define RS232RxD P15           // RS232 receive data, must be a INT input ! (INT0/INT1)
#define EnableRxInt EX0=1      // This enables Int. on the RxD input
#define DisableRxInt EX0=0     // This disables Int. on the RxD input
unsigned char RS232BitCnt;     // Bitcounter
unsigned char RS232TxData;     // Copy of data to send
unsigned char RS232RxData;     // Copy of last received data
unsigned char RS232HBaud;      // Timer high value for baudrate 
unsigned char RS232LBaud;      // Timer low value for baudrate 
bit RS232TxFlag;               // Transmit in progress
bit RS232RxFlag;               // Receive in progress
bit RS232RxRdy;                // Receive ready 
bit RS232RxErr;                // Receive error (invalid stop or start bit)

 
//----------------------------------------------------------------------------
// wait, resolution is 10 mSec
// Based on a 12 MHz XTAL 
//----------------------------------------------------------------------------
void Wait(unsigned char t) {
  unsigned int i;
  while (t--) for(i=0;i<840; i++);
}

 
//лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
// UCN5818 Helper routines.                                       19950726 CHG
// Manages a number of UCN5818 daisy chained. 
// Global constants/variables that must be declared:
// #define  BitsOut       32              // Max number of outputs 
//                                        // (number of UCN5818's * 32)
// unsigned char Output[BitsOut / 8];     // number of bytes to hold these outputs
//лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
//----------------------------------------------------------------------------
// Set a specific bit in the Output buffer to on or off...
//---------------------------------------------------------------------------- 
void SetOutput(unsigned char Num, unsigned char Value) {
 // Modify bit pattern...
  if (Value)
    Output[Num / 8] |= (1 << (Num % 8)); // Set the bit in the word...
  else 
    Output[Num / 8] &=~(1 << (Num % 8)); // Clr the bit in the word...
} 
 
//----------------------------------------------------------------------------
// Check a specific bit in the Output buffer...
//---------------------------------------------------------------------------- 
unsigned char GetOutput(unsigned char Num) {
  return (Output[Num / 8] & (1 << (Num % 8)) ? 1:0);
} 

//----------------------------------------------------------------------------
// Send all outputs out to the UCN5818 driver
//---------------------------------------------------------------------------- 
void UpdateUCN5818(void) { 
  unsigned char j;
  unsigned char dummy;
  for (j=0; j<BitsOut; j++) {
    if (GetOutput(j)) {
 //   if (Output[j / 8] & (1 << (j % 8))) {
      OutData = 1; dummy++;
      OutClk  = 1; dummy++;
    } else {
      OutClk= 1; dummy++;
    }
    OutClk = 0; OutData  = 0; dummy++;
  }
  OutStr= 1; dummy++;
  OutStr= 0; dummy++;
}
//лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл

 
//лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
// RS232 receive/transmit BitBanger helper routines               19950712 CHG
// Only half duplex allowed. 
// Note: all timing is based on a 12 Mhz Xtal !
// Resources taken:
//   Timer 0,
//   Registerbank 1
// Global constants/variables that must be declared:
// #define RS232TxD P34           // RS232 transmit data
// #define RS232RxD P15           // RS232 receive data, must be a INT input!
// #define EnableRxInt EX0=1      // This enables Int. on the RxD input
// #define DisableRxInt EX0=0     // This disables Int. on the RxD input
// unsigned char RS232BitCnt;     // Bitcounter
// unsigned char RS232TxData;     // Copy of data to send
// unsigned char RS232RxData;     // Copy of last received data
// unsigned char RS232HBaud;      // Timer high value for baudrate 
// unsigned char RS232LBaud;      // Timer low value for baudrate 
// bit RS232TxFlag;               // Transmit in progress
// bit RS232RxFlag;               // Receive in progress
// bit RS232RxRdy;                // Receive ready 
// bit RS232RxErr;                // Receive error (invalid stop or start bit)
// These constants/variables must be declared on module level.
//лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
//----------------------------------------------------------------------------
// Initialize RS232, and set baudrate (1200,2400,4800,9600,19200 baud)
// Must be called before any RS232 can take place
//----------------------------------------------------------------------------
void RS232Init(unsigned int Speed) {
  RS232TxD=1; // Idle state (is 1 on reset but...)
 // Determine the selected baudrate
 // Timer value for 9600 baud @ 12 MHz.
 // (one bit cell time)
 // (Originally: -139 at 16 MHz => 9600 baud)
 //  Formula: x=12.000.000 / (Baud *12)
 //        value=10000h - x (in hex)
 //  Values used:
 //  1200   => FCBF = -833
 //  2400   => FE61 = -415
 //  4800   => FF30 = -208
 //  9600   => FF98 = -104
 //  19200  => FFCC = -52
 
  switch (Speed) {
    case 1200:  RS232HBaud=0xFC; RS232LBaud=0xBF; break;
    case 2400:  RS232HBaud=0xFE; RS232LBaud=0x61; break;
    case 4800:  RS232HBaud=0xFF; RS232LBaud=0x30; break;
    case 9600:  RS232HBaud=0xFF; RS232LBaud=0x98; break;
    case 19200: RS232HBaud=0xFF; RS232LBaud=0xCC; break;
    // Let default be 9600 baud...
    default:    RS232HBaud=0xFF; RS232LBaud=0x98; break;
  } 
  TR=0;
  CT=0;
  GATE=0;
  EnableRxInt;// Enable external interrups for receiver
  ET0=1;      // Enable timer 0 interrups (remember to set EA elsewhere)...
              // (The timer gets started on each call to RS232Tx or when a 
              // startbit is detected on RxD input)
  TCON=0x00;
 
}

//----------------------------------------------------------------------------
// Bitwise timer interrupt on RS232, either Tx or Rx bit
//----------------------------------------------------------------------------
void RS232Int() interrupt 1 using 1 {
 // Check if we are transmitting... 
  if (RS232TxFlag) { 
   // Decrement bitcount, test for done...
    RS232BitCnt--;
    if (RS232BitCnt==0) {
      RS232TxFlag=0;      // We are done
      TR=0;               // Stop timer
    } else {
     // Test if this is the last bit (Stopbit)...
      if (RS232BitCnt==1)
        RS232TxD=1;       // Set stop bit
      else {
        RS232TxD=(RS232TxData & 1); // Send out the bit
        RS232TxData>>=1;            // Advance to next bit 
      } 
    } 
  } else {
    if (RS232RxFlag) {
   // We are receiving... 
     // Decrement bitcount
      RS232BitCnt--;
      if (RS232BitCnt) {
        // Check if we expect a startbit...
        if (RS232BitCnt==9) {
          // Check if correct state of startbit...
          if (RS232RxD) {
            RS232RxErr=1;    // Sorry, startbit not valid, report error
            RS232RxFlag=0;   // Finished with receiving
            EnableRxInt;     // Reenable Interrupts on RxD
            RS232RxRdy=1;    // We received something (although with an error)
            TR=0;            // Stop timer
          }
        // Not exspecting a startbit, so it's a databit...
        } else {
          RS232RxData >>=1; // Advance to next bit position
          if (RS232RxD) 
            RS232RxData |=0x80;
          else
            RS232RxData &=0x7F;
        }
      } else {  
        // Check if correct state of stopbit...
        if (!RS232RxD) RS232RxErr=1;    // Sorry, stopbit not valid, report error
        RS232RxFlag=0;   // Finished with receiving
        EnableRxInt;     // Reenable Interrupts on RxD
        RS232RxRdy=1;    // We received something
        TR=0;            // Stop timer
      }
    } else {
    TR=0;
    }
  }
}   

//-----------------------------------------------------------------------------
// A startbit is being received on RxD interrupt pin, setup timer for interrupt
// for each bit... 
//-----------------------------------------------------------------------------
void RS232RxInt() interrupt 0 using 2 {
  DisableRxInt;      // Disable further interrups on RxD pin,
  RS232RxFlag=1;     // mark the receiver as busy
                     // all the bits gets sampled on Timer int.
  RS232BitCnt=10;    // Bitcounter (10 bits, 1 start, 8 data, 1 stop)
//  TH=RS232HBaud;     // Set timer 0 for one-bit period
//  TL=RS232LBaud;     //
  TH=0xFF;     // Set timer 0 for HALF bit period
  TL=0x30;     //
  RTH=RS232HBaud;    //
  RTL=RS232LBaud;    //
  RS232RxData=0;     // Clear data received
  RS232RxErr=0;      // Clear last error
  TR=1;              // Start timer
  LED=!LED; 
}

//----------------------------------------------------------------------------
// Send out a byte on RS232, setup timer for bitwise interrupt, start by 
// sending out a startbit, enable timer, and finally wait for complete 
// transmission...
//----------------------------------------------------------------------------
void RS232Tx(unsigned char Data) {
  while (RS232RxFlag);  // Check that we are not in the middle of a receive...
  RS232TxFlag=1;        // mark the transmitter as busy
  RS232TxData=Data;     // Store the data to be sent
  RS232BitCnt=10;       // Bitcounter (10 bits, 1 start, 8 data, 1 stop)
  TH=RS232HBaud;     // Set timer 0 for one-bit period
  TL=RS232LBaud;     //
  RTH=RS232HBaud;    //
  RTL=RS232LBaud;    //
  TR=1;         // Start timer
  RS232TxD=0;   // send out the Startbit
  while (RS232TxFlag);  // Wait for completion of the transmission
}
 
//----------------------------------------------------------------------------
// Send out a null terminated string on RS232
//----------------------------------------------------------------------------
void RS232StringOut(char * Data) {
  while (*Data) RS232Tx(*Data++);
} 
//лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл

 
//----------------------------------------------------------------------------
// Send the contents of the Output buffer on RS232
// formatted acording to Type... 
//----------------------------------------------------------------------------
void SendBuffer(unsigned char Type) {
  unsigned char CheckSum;       // Holds calculated checksum
  unsigned char i;
  // Send the contents of the output buffer on RS232...
  RS232Tx(Type ? STX:CR);
  CheckSum=STX;
  for (i=0;i<16;i++) {
    if (GetOutput(i+16)) {
      RS232Tx(0x31);
      CheckSum+=0x31;
    } else {
      RS232Tx(0x30);
      CheckSum+=0x30;
    }
  }
  if (Type) RS232Tx(CheckSum);
  RS232Tx(Type ? ETX:LF);
}

//----------------------------------------------------------------------------
// Converts a DTMF code to its corrosponding ASCII value...
//----------------------------------------------------------------------------
char DTMFToAscii(unsigned char Code) { 
 
  if (Code>0 && Code<10) 
    return (Code+0x30);
  else {
    switch (Code) {
      case 0:  return ('D'); break;
      case 10: return ('0'); break;
      case 11: return ('*'); break;
      case 12: return ('#'); break;
      case 13: return ('A'); break;
      case 14: return ('B'); break;
      case 15: return ('C'); break;
      default: return ('?'); break;
    }
  }
} 
 
//----------------------------------------------------------------------------
// Converts a ASCII value to its corrosponding DTMF code...
//----------------------------------------------------------------------------
unsigned char DTMFFromAscii(char Code) { 
  if (Code>'0' && Code<='9') 
    return (Code-0x30);
  else {
    switch (Code) {
      case 'D': return 0; break;
      case '0': return 10; break;
      case '*': return 11; break;
      case '#': return 12; break;
      case 'A': return 13; break;
      case 'B': return 14; break;
      case 'C': return 15; break;
      default:  return 0; break;
    }
  }
} 

 
//----------------------------------------------------------------------------
// Send DTMF tone
//----------------------------------------------------------------------------
void SendDTMF(unsigned char Code) { 
  unsigned int i; 
  SetOutput(DTMFD4, Code & 0x01);  // Set databits to Tx part of DTMF tranceiver...
  SetOutput(DTMFD5, Code & 0x02);  // Set databits to Tx part of DTMF tranceiver...
  SetOutput(DTMFD6, Code & 0x04);  // Set databits to Tx part of DTMF tranceiver...
  SetOutput(DTMFD7, Code & 0x08);  // Set databits to Tx part of DTMF tranceiver...
  UpdateUCN5818();             // Set the outputs... 
  SetOutput(DTMFLATCH, 0);     // Activate Latch
  UpdateUCN5818();             // Set the outputs... 
  SetOutput(DTMFLATCH, 1);     // Remove Latch
  Wait(10);                    // Let the tones be on for 100 mSec...
  SetOutput(DTMFRESET, 1);     // Stop tones
  UpdateUCN5818();             // Set the outputs... 
  SetOutput(DTMFRESET, 0);     // All back to normal...
  UpdateUCN5818();             // Set the outputs... 
} 
 

//----------------------------------------------------------------------------
// Main part
//----------------------------------------------------------------------------                     
void main() {
  unsigned char DTMFCode;       // Holds last decodes DTMF code
  unsigned int i;               // Temporary
  char ch;                      // DTMF converted to ASCII
  unsigned char Index;          // Temporary
 
 //---------------------------------
 // Initialize outputs...
 //---------------------------------
  OutBlk=1;     // Blank all outputs from UCN5818
  OutClk=0;
  OutData=0;
  OutStr=0;
  LED=0;
 
 //----------------------------------------------------------------------------
 // Configure portpins as inputs (thats default on power up, but what the heck)
 //----------------------------------------------------------------------------
  DTMFD0=DTMFD1=DTMFD2=DTMFD3=DTMFDV=1;
  SW11=SW12=SW13=SW14=SWReset=IR=1;

 //---------------------------------
 // Configure onchip resources...
 //---------------------------------
  EA=1;        // Global int enable
 
 //----------------------------------------------------------------------------
 // Initialize RS232 channel to the correct baudrate according to SW1-3/4...
 // 1200/2400/4800 or 9600 baud...
 //----------------------------------------------------------------------------
  i=1200;
  if (SW13==SwOn) i*=2;
  if (SW14==SwOn) i*=4;
  RS232Init(i);

 //---------------------------------
 // Go ahead...
 //---------------------------------
 
  SetOutput(DTMFLATCH,1);      // Remove Latch
 
  SetOutput(DTMFLATCH,1);      // Remove Latch
  UpdateUCN5818();             // Set the outputs... 
  SetOutput(DTMFRESET,1);      // Stop tone
  UpdateUCN5818();             // Set the outputs... 
  SetOutput(DTMFRESET,0);      // All done
  UpdateUCN5818();             // Set the outputs... 
 // Say hello
 //  RS232StringOut("\0x0D\0x0ADTMF-VCR (C) KIA 1995\0x0D\0x0A\0x00");
 // Enable UCN5818 outputs 
  OutBlk=0;  
 
  //----------------------------------------------------------------------------
  // Forever
  //----------------------------------------------------------------------------
  while(1) {
    //----------------------------------------------------------------------------
    // Check reset button, if activated, reset all relays
    //----------------------------------------------------------------------------
    if (SWReset==SwOn) {
      for (i=0;i<16;i++) SetOutput(16+i, 0);
      UpdateUCN5818();       // Update outputs 
      SendBuffer(SW11);      // And send new contents on RS232
      while (SWReset==SwOn); // Wait until Reset signal goes away again...
      RS232Tx('R');
    }  
 
    //----------------------------------------------------------------------------
    // if a valid DTMF code has been signaled, go decode it...
    //----------------------------------------------------------------------------
    if (DTMFDV) {
      LED=IR;
      // Calculate DTMF code...
      DTMFCode=(DTMFD3 ? 8:0) + (DTMFD2 ? 4:0) + (DTMFD1 ? 2:0) + (DTMFD0 ? 1:0);
      // Simple output on RS232
      if (SW12==SwOn) {
        RS232Tx(DTMFToAscii(DTMFCode));
        for (i=0; i<1000; i++);// sleep a little...
      } else {
      // working on the relays...
        if (GetOutput(16+DTMFCode))     // Toggle output
          SetOutput(16+DTMFCode, 0);
        else
          SetOutput(16+DTMFCode, 1);
        UpdateUCN5818();       // Update outputs 
        SendBuffer(SW11);      // And send new contents on RS232
      }
      while (DTMFDV); // Wait until Dataavailable signal goes away again...
    } // if (DTMFDV)

    //----------------------------------------------------------------------------
    // If something received on RS232, check it...
    //----------------------------------------------------------------------------
    if (RS232RxRdy) {
      RS232RxRdy=0; // we have seen it...
      // If no error...
      if (!RS232RxErr) {
        //----------------------------------------------------------------------------
        // Check the mode, either simple DTMF tone output as received on RS232, or 
        // setting of the relays...
        //----------------------------------------------------------------------------
        if (SW12==SwOn) {
          // Simple DTMF tones...
          SendDTMF(DTMFFromAscii(RS232RxData));
          RS232Tx(RS232RxData);
        } else {
          // Setting of the relays...
          // Check correct STX start condition...
          if ((RS232RxData==STX && SW11==SwOff) || (RS232RxData==CR && SW11==SwOn)) {
            // Prepare for the first char...
            Index=0;
            // Read the next 16 bytes, and set the outputs
            while (Index<16) {
              while (!RS232RxRdy); // wait for next character...
              // Only update output if no receive error...
              if (!RS232RxErr)
                SetOutput(16+Index, (RS232RxData=='1' ? 1:0));//Set output according 
              Index++; // Next position                      //to received character
              RS232RxRdy=0;
            }
            while (!RS232RxRdy); // wait for the terminating stuff, Checksum
            RS232RxRdy=0;
            while (!RS232RxRdy); // wait for the terminating stuff, ETX
            RS232RxRdy=0;
            UpdateUCN5818();       // Update outputs 
            SendBuffer(SW11);      // And send new contents on RS232
          } // while (Index<16)
        } // if (RX232Err)
      } // if (!RS232Err)
    }
  }
}
