// ---------------------------------------------------------------------
// 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
}
}
}
+
Questions:
| file: /Techref/microchip/16F/628/uartinttst-rh.htm, 15KB, , updated: 2008/1/13 02:52, local time: 2025/10/22 19:51,
owner: RH-planet-b,
216.73.216.20,10-1-5-169:LOG IN
|
| ©2025 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://massmind.ecomorder.com/techref/microchip/16F/628/uartinttst-rh.htm"> Rob Hamerling's 57.6kbps interrupt driven 16F628 test circuit and program</A> |
| Did you find what you needed? |
Welcome to ecomorder.com! |
Welcome to massmind.ecomorder.com! |
.