/***************************************************************************/ /* RS232.C An Interrupt driven Asyncronous Serial Port */ /* */ /* Version : 2.3 */ /* */ /* By : Julian Winpenny */ /* */ /* Date : 24/3/2003 */ /* */ /* 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 */