 
  
Code:
 
// ---------------------------------------------------------------------
// S628.c     Study/experiment with interrupt driven serial
//            communications with a PIC-equiped device as DCE.
//
// Author:    Rob Hamerling.
// Date:      February 2004.
// E-mail:    info@robh.nl 
// homepage:  http://www.robh.nl
// ---------------------------------------------------------------------
//
//  Function: echo incoming datastream from DTE back to 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).
//  - While DTE inactive (RTS false) the PIC slumbers. It gives a 'being
//    alive' signal by slowly flashing the RTS light. It is waked-up by
//    RB0 (to which RTS is connected)
//  - 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 'inversed' logic the signals are called
//   here CTSinv and RTSinv.
//
// -------------------------------------------------------------------------
//  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  &= ~0b11.1111.1111.1111        // all OFF
#pragma  config  |=  0b11.1111.0110.0110
//                                x   xx        FOSC = HS
//                                   x          WDT enabled
//                              x   x           BOD enabled (forces /PWRTE)
//                             x                LVP disabled (makes RB4 free)
//                     xxxxxxx                  no memory protection
#pragma  config ID = 0x6281                     // firmware ID (optional)
#pragma  bit CTSinv  @ PORTA.2                  // CTS signal to DTE (PC)
#pragma  bit RTSinv  @ PORTB.0                  // RTS signal from DTE (PC)
#pragma  bit RTSled  @ PORTB.3                  // visual RTS signal
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  TMR1COUNT   (OSCFREQ/4/1000)           // 16-bits count for 1 ms
                                                // (prescaler 1:1)
#define  BPSRATE     57600                      // desired speed
#define  BPSCLASS    TRUE                       // BRGH setting (high)
#define  BPSCOUNT    ((10*OSCFREQ/16/BPSRATE-5)/10)  // SPBRG (BRGH=1)
                                                // closest integer value
#define  XMTBUFSIZE  32                         // output buffer size
#define  RCVBUFSIZE  64                         // input buffer size
#define  DELTA       17                         // minimum free rcv buffer ..
                                                // .. space (PC UARTFiFo + 1)
int8     xmtoffset;                             // offset next byte to xmit
int8     putoffset;                             // offset last appl. out byte
int8     rcvoffset;                             // offset next byte to receive
int8     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
      if (++xmtoffset >= XMTBUFSIZE)            // update offset
        xmtoffset = 0;                          // wrap
      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?)
      getoffset = 0;                            // flush buffers
      xmtoffset = 0;
      putoffset = 0;
      rcvoffset = 1;
      rcvbuf[0] = RCREG;                        // move byte to rcv buffer
      CTSinv = TRUE;                            // ensure CTS is true
      }
    else {                                      // data without errors
      rcvbuf[rcvoffset] = RCREG;                // move byte to rcv buffer
      x = rcvoffset + 1;                        // offset next byte
      if (x >= RCVBUFSIZE)                      // beyond buffer boundary
        x = 0;                                  // wrap to begin
      if (x != getoffset)                       // buffer not yet full
        rcvoffset = x;                          // update offset,
                                                // (else discard byte,
                                                //  CTS flow control failed)
      if (CTSinv == FALSE) {                    // CTS is TRUE
        x = getoffset - rcvoffset;              // offset difference
        if (x <= 0)                             // wrapping effect
          x += RCVBUFSIZE;                      // wrapping correction
        if (x < DELTA)                          // buffer reaches 'full'
          CTSinv = TRUE;                        // make CTS FALSE
        }
      }
    }
  if (INTE == TRUE && INTF == TRUE) {           // RB0 change interrupt
                                                // nothing to do, just ..
    INTF = FALSE;                               // .. wake-up from sleep
    }
  /* Note: Other interrupts disabled, so no further checks needed */
  FSR = save_FSR;                               // restore FSR
  int_restore_registers                         // restore other
  }
// -----------------------------------------------
//  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;                          // next char
    if (x >= XMTBUFSIZE)                        // beyond buffer boundary
      x = 0;
    while (x == xmtoffset)                      // buffer full!
      ;                                         // spin until something xmit'd
    putoffset = x;                              // update offset
    TXIE = TRUE;                                // (re-)enable xmit interrupts
    }
  }
// ----------------------------------------------------------------
//  copy input bytes to callers buffer
//     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
    if (++getoffset >= RCVBUFSIZE)              // update offset
      getoffset = 0;
    }
  if (CTSinv == TRUE) {                         // (CTS is FALSE)
    x = getoffset - rcvoffset;                  // offset difference
    if (x <= 0)                                 // wrapping effect
      x += RCVBUFSIZE;                          // wrapping correction
    if (x >= DELTA)                             // enough free space now
      CTSinv = FALSE;                           // (make CTS TRUE)
    }
  return i;                                     // number of bytes returned
  }
// -------------------------------------------------------
//  Perform all required initial PIC setup
// -------------------------------------------------------
static void setup() {
  CMCON   = 0b0000.0111;                        // Comparator off
  CCP1CON = 0b0000.0000;                        // Capt/Comp/PWM off
  OPTION  = 0b0000.1111;                        // WDT prescaler 1:128
  T1CON   = 0b0000.0001;                        // Timer1 enabled, presc 1:1
  INTCON  = 0;                                  // all interrupt bits off
  PIR1    = 0;                                  //  ..
  INTEDG  = 0;                                  // int at falling edge RB0
                                                // (= rising of RTS!)
  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
  INTE    = TRUE;                               // RB0 (RTSinv) change
  GIE     = TRUE;                               // globally enable interrupts
  }
// -------------------------------------------------------------------
//  Milliseconds delay by using TMR1 as 16-bits counter
//
//  See top of source for the value of TMR1COUNT
// -------------------------------------------------------------------
static void msdelay(char millisec) {
  uns16 usTimeCount;                            // value of Timer1
  do  {
    TMR1H = 0;                                  // restart Timer1 ..
    TMR1L = 0;                                  // .. counting
    do {
      usTimeCount = (uns16)TMR1H << 8;          // take high byte value
      usTimeCount += TMR1L;                     // aad low byte value
      } while (usTimeCount < TMR1COUNT);        // pause 1 millisecond
    } while (--millisec > 0);                   // number of milliseconds
  }
// -------------------------------------------------------------------
//  Keep PIC in slumbering state: sleep most of the time
//
//  Flash a LED as visual 'being alive' signal
//
// -------------------------------------------------------------------
static  void  waitforRTS(void) {
  char  ucOptionSave;                           // option reg at entry
  ucOptionSave = OPTION;                        // save OPTION register
  while (RTSinv == TRUE) {                      // waiting for RTS
    OPTION = 0b0000.1111;                       // WDT postscaler 1:128
    sleep();                                    // wait for RB0 or Watchdog
    RTSled = TRUE;                              // RTS LED on
    OPTION = 0b0000.1001;                       // WDT postscaler 1:2
    sleep();                                    // duration of RTS LED flash
    RTSled = FALSE;                             // RTS LED off
    }
  OPTION = ucOptionSave;                        // restore OPTION to original
  }
// ===============================================================
//
//   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
  const  char *welcome = "Echo from S628\n\r";  // welcome!
  setup();                                      // init PIC
  for (;;) {                                    // forever
    RTSled = FALSE;                             // assume RTS false
    CTSinv = TRUE;                              // CTS FALSE
    waitforRTS();                               // in slumbering state
    RTSled = TRUE;                              // show RTS status
    CTSinv = FALSE;                             // CTS TRUE
    xmtoffset = 0;                              // (re-)init ..
    putoffset = 0;                              //  .. input and ..
    rcvoffset = 0;                              //   .. output ..
    getoffset = 0;                              //    .. buffer offsets
    for (i=0; welcome[i] != '\0'; i++) {        // copy msg to I/O buffer
      k = welcome[i];
      buffer[i] = k;
      }
    putdata(buffer, i);                         // send msg to DTE
    while (RTSinv == FALSE) {                   // RTS true
      l = getdata(buffer, sizeof(buffer));      // get input
      if (l > 0)                                // something received
        putdata(buffer, l);                     // echo the input
      else                                      // nothing received
        msdelay(25);                            // do 'low priority' work
      clrwdt();                                 // reset watchdog
      }
    }
  }
+
Interested:
See:
Comments:
BUg on lineJames Newton replies: Ah! Very common and annoying error. X is being ASSIGNED the value of RCVBUFSIZE rather then being compaired to it. It should be "if (x==RCVBUFSIZE)" and the best coding style to avoid makeing this mistake is to always put the constant first. E.g. "if (RCVBUFSIZE==X)" becuase if you only put in one "=" the compiler will show you the error.+
if (x = RCVBUFSIZE) // beyond buffer boundary
x = 0; // wrap to begin
note the missing = in the if statement drove me mad for we while!
| file: /Techref/microchip/language/c/S628.htm, 20KB, , updated: 2007/10/7 14:15, local time: 2025/10/31 12:27, 
owner: RH-planet-b, 
 
216.73.216.212,10-3-97-86: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://techref.massmind.org/techref/microchip/language/c/S628.htm"> Interrupt driven routines, using PIC hardware USART,with CTS flow control. Source code for CC5X C compiler with amplecomments, easily adaptable to other PICs than the sample 16F628.</A> | 
| Did you find what you needed? | 
| Welcome to massmind.org! | 
| The Backwoods Guide to Computer Lingo | 
.