/***************************************************************************/
/*  RS232.C     An Interrupt driven Asyncronous Serial Port                */
/*                                                                         */
/*  Version :   2.2                                                        */
/*                                                                         */
/*       By :   Julian Winpenny                                            */
/*                                                                         */
/*     Date :   20/3/1999                                                  */
/*                                                                         */
/*  Purpose :   Asyncronous Transmitter & Receiver                         */
/*                                                                         */
/*     Mode :   9600 Baud, 1 stopbit, No parity                            */
/*                                                                         */
/*                                                                         */
/*                                                                         */
/***************************************************************************/



/************************/
/* Function prototypes  */
/************************/

void Setup(void);
void TxNextBit(void);
void PutChar(char);
char GetChar(void);
void StartBitDetect(void);
void RcvNextBit(void);

/********************************/
/*  Global Register assignments */
/********************************/

char TxByte;                  // The Byte to Transmit
char RxByte;                  // The Received Byte
char TxRxBitCount;            // Bit counter for TX/RX
char SerialStatus;            // Serial status register
char data;                    // General purpose register

/*************************/
/* Flag assignments      */
/*************************/

/* Bits of SerialStatus  */
#define TxOver       0        // Set when byte transmitted
#define TxEnable     1        // Set to enable transmitter
#define RxOver       2        // Set when byte received
#define RxFrameError 3        // Set when stop bit not zero
#define RxStarted    4        // Set when receive character is in progress
#define RxBit        5        // Unused.


/*************************/
/* Bit Mask definitons   */
/*************************/

#define RxInputMask      0x10; // Receiver input pin is connected to RA4,
                               // ie. bit 4 of Port A
#define TxOverMask       0x01;
#define TxEnableMask     0x02;
#define RxOverMask       0x04;
#define RxFrameErrorMask 0x08;
#define RxStartedMask    0x10;                    
#define RxBitMask        0x20;
                             
#define IntconRtcc       0x04;                             

/******************************/
/* TxRxBitCount event numbers */
/* ( Transmit mode )          */
/******************************/

#define StartBit 10
#define Bit0 9
#define Bit1 8
#define Bit2 7
#define Bit3 6
#define Bit4 5
#define Bit5 4
#define Bit6 3
#define Bit7 2
#define StopBit 1

/********************************/
/* Port and system assignments  */
/********************************/

#define Rs232Out 1          // Rs232 output port bit


//#define Baud9600Rate 207         // TMR0 Rate for 4 Mhz clock
//#define Baud9600RatePlusHalf 159 // Start bit to first data bit delay (4Mhz)
                                   // 
                                   //        *** May need adjustment ***
                                   //     Very critical for High Baud Rates -
                                   //     - at low clock speed

#define Baud9600Rate 154           // TMR0 Rate for 8 Mhz clock
#define Baud9600RatePlusHalf 130   // Start bit to first data bit delay (8Mhz)

#define Rs232BitMask 1             // Mask for RS232 send TxNextBit()

#define PortAConfig  0x14          // Configure port A ( 1 = input )
                                   // RA4/RTCC = Input / RA2 = Input
                                  
#define PortBConfig  0xff          // Configure port B  ( All Input )
#define TxMode       0x40          // Set option reg TMR0 interrupt source from counter.
                                   // Max speed prescaler ( 1:2 )
#define RxMode  TxMode     
#define RxStartbitMode 0x78        // TMR0 source is External Clock (falling edge), 
                                   // Assign prescaler to Watchdog timer
                                   // - for detection of START BIT

#define POSLOGIC  1                // When defined then USE TRUE RS232 LEVELS ( For Transmitter ) 
     
                           
                                    
/***************************************************************/
/*               Start of MAIN                                 */
/***************************************************************/

main()
{
 
  const char Message[] = { 'H', 'e', 'l', 'l', 'o', '!', 13, 10, 0x00 };
 
  char a;
  
  Setup();                                           // Set-up the system.

  set_bit( SerialStatus, TxEnable );                 // *** Enable Transmitter ***

  a = 0;
  
   while ( Message[a] != 0x00 ) 
       {
         PutChar( Message[a++] );                    // Send the Message
         while ( ( SerialStatus & TxOverMask ) == 0 ); 
       }  

 
 
   while(1)
      {
         data = GetChar();                           // Receive the next Character
                                
        if ( ( SerialStatus & RxFrameErrorMask ) == 0 )  // Is it a valid character ?
           {
           
             PutChar(data);                          // Echo the Received byte
             
             while ( ( SerialStatus & TxOverMask ) == 0 ); 
                                                     // Wait for Character to be transmitted
           }                                  
      } 
     
  
     
}

// End of Main 


/************************************************************/
/*             Process any interrupts                       */
/************************************************************/

void interrupt( void )
{

                                   
    if ( ( INTCON & IntconRtcc ) != 0 )  // If the RTCC times-out
     {                             
                                         // If the transmitter is enabled
                                         // - then begin to transmit
                                         
      if ( ( SerialStatus & TxEnableMask ) != 0 )
           {                       
               TxNextBit();             // Send next bit.
           }
                                        // If no character is being received
      else if ( ( SerialStatus & RxStartedMask ) == 0 ) 
           {
             StartBitDetect();          // Initialise Receiver ( wait for a start bit )
           } 
            
      else {
              
              RcvNextBit();             // Else receive the next bit
           }
                                   
           
     }      

// Return from Interrupt     
}
// End of Interrupt Service routine


/*******************************/
/* Setup Ports and Interrupts  */
/*******************************/
void Setup(void)
{
  set_bit( STATUS, RP0 );
  OPTION_REG = TxMode;              // Set Option register to transmitter mode
  set_tris_a( PortAConfig);         // Setup ports
  set_tris_b( PortBConfig );
  clear_bit( STATUS, RP0 );
 
#ifdef POSLOGIC                     // Set the RS232 output to "Stop bit"
    output_low_port_a( Rs232Out );  
#else  
    output_high_port_a( Rs232Out ); 
#endif

  TxRxBitCount = StartBit;          // Setup bit count
  SerialStatus = 0;                 // Clear other flags
  set_bit( SerialStatus, TxOver );  // Set the TxOver status for 'Ready'
  clear_bit( INTCON, T0IF );        // Clear TMR0 overflow flag
  disable_interrupt( T0IE );        // Disable TMR0 overflow bit
  enable_interrupt( GIE );          // Enable Global Interrupts

}

/********************/
/* Send a character */
/********************/
void PutChar(char ch)
{
                                        // Wait for stop bit of
  while ( ( SerialStatus & TxOverMask ) == 0 ); 
                                        // last character to be transmitted
  clear_bit( SerialStatus, TxOver );    // Reset TxOver flag
  TxByte = ch;                          // Setup the data byte to send
  TMR0 = Baud9600Rate;
  clear_bit( INTCON, T0IF );            // Clear TMR0 overflow flag
  enable_interrupt( T0IE );             // Enable TMR0 overflow bit to -
                                        // - initiate transmission  #
  while ( TxRxBitCount != Bit0 );       // Wait for start bit done.                        
     
}

/*******************************************/
/* Send next bit Interrupt service routine */
/*******************************************/
void TxNextBit(void)
{
       
       
   if ( TxRxBitCount == StartBit )
    {                                              // Send the start bit
       #ifdef POSLOGIC
          output_high_port_a( Rs232Out );          // True RS232
       #else
          output_low_port_a( Rs232Out );           // Inverted RS232 
       #endif
    }
               
                                                   // Send the data bits 
                               
   if ( ( TxRxBitCount <= 9 ) && ( TxRxBitCount >= 2 ) )
   
       {
         if (  ( TxByte & Rs232BitMask ) == 0 )    // Test bit 0 of data to be sent
            {
               #ifdef POSLOGIC
                  output_high_port_a( Rs232Out );  // True RS232
               #else
                  output_low_port_a( Rs232Out );   // Inverted RS232 
               #endif
            }
         else
            {
               #ifdef POSLOGIC      
                  output_low_port_a( Rs232Out );   // True RS232
               #else
                  output_high_port_a( Rs232Out );  // Inverted RS232
               #endif            
            }
                   
            TxByte = ( TxByte >> 1 );              // Get next bit to send.....
           
        }         
                                                  // Send the stop bit
    if ( TxRxBitCount == StopBit )
      {
        #ifdef POSLOGIC      
             output_low_port_a( Rs232Out );       // True RS232
        #else
             output_high_port_a( Rs232Out );      // Inverted RS232
        #endif            
      }  
       
   
  TxRxBitCount--;                                 // count down the bit counter

 if ( TxRxBitCount == 0 ) 
    {
      TxRxBitCount = StartBit;
      set_bit( SerialStatus, TxOver );
      clear_bit( INTCON, T0IF );                  // Clear TMR0 overflow flag
      disable_interrupt( T0IE );                  // Disable TMR0 overflow bit
      return;                                     // Return with Interrupt disabled
    }  
  
    
    TMR0 = TMR0 + Baud9600Rate;
    clear_bit( INTCON, T0IF );                    //clear TMR0 overflow flag
    enable_interrupt( T0IE );                     //enable TMR0 overflow bit

}
// end of TxNextBit(void)

               /****************************/
               /* Start of Receiver code   */  
               /****************************/


/************************************************/
/* Prepare receiver for next character          */
/************************************************/
char GetChar(void)
{

       SerialStatus = 0;                    // Clear Status flags
       TxRxBitCount = 9;                    // 8 data bits + 1 stop bit
       RxByte = 0;                          // Clear Rxbyte
       set_bit( STATUS, RP0 );
       OPTION_REG = RxStartbitMode;         // Set Option register to receiver mode
       clear_bit( STATUS, RP0 );
 
       TMR0 = 0xff;                         // Wait for start bit.
       clear_bit( INTCON, T0IF );           // Clear TMR0 overflow flag
       enable_interrupt( T0IE );            // Enable TMR0 overflow bit

       while( ( SerialStatus & RxOverMask ) == 0 ); // Wait for character ready
              
       set_bit( SerialStatus, TxEnable );   // Enable Transmitter-
       set_bit( SerialStatus, TxOver );     // -ready for next tx byte
       TxRxBitCount = StartBit;
       clear_bit( INTCON, T0IF );           // Clear TMR0 overflow flag
       disable_interrupt( T0IE );           // Disable TMR0 overflow bit
       return RxByte;                       // Return with the received character
 
}
// End GetChar()


/**************************************************/
/* Detect the initial start bit of each character */
/**************************************************/              
void StartBitDetect(void)
{
                                            // If RxInput is 0
   if ( ( input_port_a() & RxInputMask ) == 0 )
       {                                    // Start bit detected
                  
         set_bit( SerialStatus, RxStarted); // Set Rx started flag
         set_bit( STATUS, RP0 );            // Set Option register to -
         OPTION_REG = RxMode;               // - wait for first data bit
         clear_bit( STATUS, RP0 );
         TMR0 = Baud9600RatePlusHalf;       // Set the timer to wait 1.5 x bit time
         clear_bit( INTCON, T0IF );         // Clear TMR0 overflow flag
         enable_interrupt( T0IE );          // Enable TMR0 overflow bit
       }
       
   else
    {                                       // False Start bit detected.
   
  FalseStartBit:

     TMR0 = 0xff;
     set_bit( STATUS, RP0 );
     OPTION_REG = RxStartbitMode;           // Set Option register to receiver mode
     clear_bit( STATUS, RP0 );
     clear_bit( SerialStatus, RxStarted );
     clear_bit( INTCON, T0IF );             // Clear TMR0 overflow flag
     enable_interrupt( T0IE );              // Enable TMR0 overflow bit
    }
 
}
// end of StartBitDetect()


/**************************************************/
/* Receive the next bit of the current character  */
/**************************************************/
void RcvNextBit(void)
{
  
   
  if ( TxRxBitCount == 1 )             // Is this the stop bit ?
     {
        
       if ( ( input_port_a() & RxInputMask ) == 0 )   
          {                                 // Framing error! Stop bit was not '1'
            set_bit( SerialStatus, RxFrameError );
          }
          
       clear_bit( SerialStatus, RxStarted );// Stop further RTCC interrupts.
       set_bit( SerialStatus, RxOver );     // Signal GetChar() that byte available
       disable_interrupt( T0IE );           // Disable TMR0 overflow bit
       clear_bit( INTCON, T0IF );           // Clear TMR0 overflow flag 
                                            // Receive done
     }
  
                 // if ( ( TxRxBitCount <= 9 ) && ( TxRxBitCount >= 2 ) )    
 else 
     {
       asm rrf _RxByte, F;                  // Shift RxByte right, for next bit
       
       if (  ( input_port_a() & RxInputMask )  != 0 )
            {
              set_bit( RxByte, 7 );
            }  
       else  
            {
              clear_bit( RxByte, 7 );
            }  
           
       TMR0 = TMR0 + Baud9600Rate; 
       clear_bit( INTCON, T0IF );           // Clear TMR0 overflow flag
       enable_interrupt( T0IE );            // Enable TMR0 overflow bit
     }     

   TxRxBitCount--;                          // Count down bits received.
   
}
// End of RcvNextBit


/*   EOF   */ 
