// --------------------------------------------------------------------- // S628.c Study/experiment with interrupt driven serial // communications with a PIC-equiped device as DCE. // // Author: Rob Hamerling. // Date: January 2003. // E-mail: robh@hccnet.nl // homepahe: http://www.robh.nl // --------------------------------------------------------------------- // // Function: echo incoming datastream from DTE. // Features: // - Interrupt driven, high speed, full duplex data flow (57600 bps) // - Use of builtin USART in RS232 mode (8 bits, no parity). // - With relatively large receive buffer. // - Using CTS flow control (PC -> PIC). // PC-side should have set CTS output flow control enabled, // PC FiFo transmit load count may be set to 16 (max). // - Note: no RTS flow control (PIC -> PC)! // // Language support: CC5X compiler version 3.1 // // Hardware: PIC 16F628 or similar with UART, and MAX232. // // // --------------------------------------------------------------------- // // Simplified schematics: // COMx plug // PIC16F628 MAX232 RS232 DB9 DB25 // +-------------+ +-----------+ // | | | | // | RA2 (1)|-----|(11)---(14)|--->-- CTS --- 8 5 // | RB0 (6)|-----|(12)---(13)|---<-- RTS --- 7 4 // | | | | // | RB1 (7)|-----|(9)-----(8)|---<-- TxD --- 3 2 // | RB2 (8)|-----|(10)----(7)|--->-- RxD --- 2 3 // | | | | // | (5) | | (15) | // +------|------+ +----|------+ // +-----------------+----------- GND --- 5 7 // // +-< DTR --- 4 20 // Optional cable wraps: | // (maybe required by PC softw.) +-> DSR --- 6 6 // +-> DCD --- 1 8 // // --------------------------------------------------------------------- // Some basic PIC and RS232 knowledge will be needed to fully understand // the data flow and control signalling used in this program. // // The PIC ports use positive logic: // '1' is positive voltage, '0' is ground. // // This program uses positive logic for boolean variables: // the symbol TRUE for '1', the symbol FALSE for '0'. // // In the RS232 standard: // - Negative voltage ('mark') means OFF for control signals, and // indicates 1 (one) for a data signals (start-, data-, stop-bits). // - Positive voltage ('space') means ON for control signals and // 0 (zero) for start-, data- and stop-bits. // // Since the MAX232 is not only a level convertor (between TTL and RS232) // but also a signal inverter, you should be aware of the following: // - The inversion of PIC data-in and data-out by the MAX232 is required // to convert data-, start- and stop-bits to/from the corresponding // RS232 polarity. So nothing special has to be done in the program. // - For RTS and CTS the inversion by the MAX232 inversion is NOT desired, // and therefore the program uses inverted signaling for RTS and CTS: // 'FALSE' is used for ON and 'TRUE' for OFF with RTS/CTS signals! // As a reminder for this 'reversed' logic the signals are called // here CTSrev and RTSrev. // // ------------------------------------------------------------------------- // For other examples and useful learning material see also: // - MicroChip datasheets (for the PIC16F62X: DS30400C). // - Tony Kubek's example for the PIC16F876, // ASM, interrupt driven, no CTS flow control, single byte buffer. // - Fr. Thomas MacGhee's example for the PIC16C74, // ASM, not interrupt driven, but contains many educational notes. // ------------------------------------------------------------------------- #pragma chip PIC16F628 // target PIC #include <int16cxx.h> // interrupt support #pragma config |= 0x3FFF // all on initially #pragma config &= ~0x0080 // LVP off -> RB4 I/O #pragma config FOSC=HS // 20MHz crystal #pragma config WDTE=off // watch dog disabled #pragma config ID=6280 // firmware ID (optional) #pragma bit CTSrev @ PORTA.2 // CTS signal to DTE (PC) #pragma bit RTSrev @ PORTB.0 // RTS signal from DTE (PC) typedef bit BOOL, BOOLEAN; // boolean variable type(s) #define FALSE 0 // PIC: off, low #define TRUE 1 // PIC: on, high #define OSCFREQ 20000000 // oscillator frequency #define TMR0COUNT (OSCFREQ/8/16/1000) // for 1 ms delay (OPTION = 4) #define BPSRATE 57600 // desired speed #define BPSCLASS TRUE // BRGH setting (high) #define BPSCOUNT ((10*OSCFREQ/16/BPSRATE-5)/10 - 1) // SPBRG (BRGH=1) // closest integer value #define XMTBUFSIZE 32 // (power of 2) output buffer size #define RCVBUFSIZE 64 // (power of 2) input buffer size #define DELTA 17 // minimum free rcv buffer .. // .. space (PC UARTFiFo + 1) char xmtoffset; // offset next byte to xmit char putoffset; // offset last appl. out byte char rcvoffset; // offset next byte to receive char getoffset; // offset last appl. in byte char xmtbuf[XMTBUFSIZE]; // circular output buffer bank1 char rcvbuf[RCVBUFSIZE]; // circular input buffer // located in RAM bank1! // ---------------------------- // Interrupt service routine // ---------------------------- #pragma origin 4 // hardware requirement extern interrupt isr(void) { char save_FSR; // FSR save byte char x; // intermediate byte value int_save_registers // save registers save_FSR = FSR; // save FSR if (TXIF == TRUE && TXIE == TRUE) { // RS232 transmit interrupt if (xmtoffset != putoffset) { // still data in xmit buffer x = xmtbuf[xmtoffset]; // next char to xmit xmtoffset = (xmtoffset + 1) & (XMTBUFSIZE - 1); // update offset if (xmtoffset == putoffset) // was this last byte? TXIE = FALSE; // disable xmit interrupts TXREG = x; // now actually xmit char } } if (RCIF == TRUE && RCIE == TRUE) { // RS232 receive interrupt if (OERR == TRUE) { // overrun, reset UART CREN = FALSE; // disable UART CREN = TRUE; // re-enable UART } // discard pending bytes else if (FERR == TRUE) // framing error (break?) x = RCREG; // read and discard byte else { // data without errors rcvbuf[rcvoffset] = RCREG; // move byte to rcv buffer x = (rcvoffset + 1) & (RCVBUFSIZE - 1); // offset next byte if (x != getoffset) // buffer not yet full rcvoffset = x; // update offset, // (else discard byte, // CTS flow control failed) if (CTSrev == FALSE) { // CTS true! if (rcvoffset > getoffset) // circular buffer situation x = RCVBUFSIZE - rcvoffset + getoffset; // free buffer space else // other situation x = getoffset - rcvoffset; // free buffer space if (x <= DELTA) { // buffer reaches 'full' CTSrev = TRUE; // drop CTS (CTS FALSE) } } } } /* Note: All other interrupts disabled, so no further checks needed */ FSR = save_FSR; // restore FSR int_restore_registers // restore other } // ------------------------------------------------------------------- // Milliseconds delay by polling TMR0 // // See top of source for the calculation of TMR0COUNT, // depending on oscillator frequency and prescaling via OPTION. // ------------------------------------------------------------------- static void msdelay(char millisec) { do { TMR0 = 0; while (TMR0 < TMR0COUNT) // pause of 1 millisecond ; } while (--millisec > 0); // number of milliseconds } // ----------------------------------------------- // copy output bytes of caller // from: application buffer // to: interrupt controlled transmit buffer // // returns nothing // // notes: - initiates transmission (interrupt handler) // when not currently transmitting // - spin when transmission buffer full // (wait for free buffer space) // ----------------------------------------------- static void putdata(char *buffer, char bytesout) { char i; // counter(s) char x; // intermediate byte value for (i=0; i<bytesout; i++) { // all user data x = buffer[i]; // copy char xmtbuf[putoffset] = x; // .. to buffer x = (putoffset + 1) & (XMTBUFSIZE - 1); // offset next char while (x == xmtoffset) // buffer full! ; // spin until something xmit'd putoffset = x; // update offset TXIE = TRUE; // (re-)enable xmit interrupts } } // ---------------------------------------------------------------- // copy input bytes to caller // from: interrupt controlled receive buffer // to: application buffer // returns: number of bytes actually stored in application buffer // // notes: - rise CTS when receive buffer has more than <DELTA> // bytes free space after delivering data to caller. // ---------------------------------------------------------------- static char getdata(char *buffer, // application buffer char bufsize) { // size of appl. buffer char i, x; for (i=0; i<bufsize; i++) { // fill user buffer (max) if (getoffset == rcvoffset) // no more data break; x = rcvbuf[getoffset]; // copy char buffer[i] = x; // .. to user buffer getoffset = (getoffset + 1) & (RCVBUFSIZE - 1); // update offset } // to caller if (CTSrev == TRUE) { // CTS FALSE) if (rcvoffset > getoffset) // circular buffer situation x = RCVBUFSIZE - rcvoffset + getoffset; // free buffer space else // other situation x = getoffset - rcvoffset; // free buffer space if (x >= DELTA) { // enough free space now CTSrev = FALSE; // rise CTS (CTS TRUE) } } return i; // number of bytes returned } // ------------------------------------------------------- // Perform all required PIC setup // ------------------------------------------------------- static void setup() { CMCON = 0b0000.0111; // Comparator off CCP1CON = 0b0000.0000; // Capt/Comp/PWM off OPTION = 3; // TMR0 prescaler 1:16 INTCON = 0; // all interrupt bits off PIR1 = 0; // .. PORTA = 0; // all ports zero PORTB = 0; // .. TRISA = 0b0010.0000; // IN: RA5/MCLR TRISB = 0b0001.0011; // IN: RB0,1,4 PIE1 = 0; // disable all ext. interrupts BRGH = BPSCLASS; // baudrate class SPBRG = BPSCOUNT; // baudrate clock divisor TXEN = TRUE; // enable UART transmit SYNC = FALSE; // async mode RCIE = TRUE; // enable receive interrupts SPEN = TRUE; // enable UART CREN = TRUE; // enable UART receive PEIE = TRUE; // enable external interrupts GIE = TRUE; // globally enable interrupts } // =============================================================== // // M A I N L I N E // // Initially CTS is set false and the program waits for // RTS to become true before activating the echo loop. // When RTS become true, CTS follows, which allows the // DTE to send data. The echo loop remains active as // long as RTS remains true. When RTS becomes false the // echo-loop is terminated and the PIC reset to its initial // state, waiting for RTS. // // =============================================================== extern void main(void) { char i, k, l; // counter(s) char buffer[20]; // local I/O buffer setup(); // init PIC for (;;) { // forever CTSrev = TRUE; // CTS FALSE while (RTSrev == TRUE) // wait for rise of RTS (DTE) ; CTSrev = FALSE; // CTS TRUE xmtoffset = 0; // (re-)init .. putoffset = 0; // .. input and .. rcvoffset = 0; // .. output .. getoffset = 0; // .. buffer offsets while (RTSrev == FALSE) { // RTS TRUE l = getdata(buffer, sizeof(buffer)); // get input if (l > 0) // something received putdata(buffer, l); // echo the input else // nothing msdelay(10); // do 'low priority' work } } }
Interested:
Questions: