ON 20030201@3:28:34 PM at page: http://www.piclist.com/techref/microchip/language/c/ios.htm RH-planet-b Rob Hamerling I have agreed to maintain this page. ON 20030204@9:53:13 AM at page: http://www.piclist.com/techref/microchip/language/c/ios.htm RH-planet-b Rob Hamerling Code: http://www.piclist.com/techref/microchip/language/c/S628.htm
Interrupt driven routines, using PIC hardware USART,
with CTS flow control. Source code for CC5X C compiler with ample
comments, easily adaptable to other PICs than the sample 16F628.
ON 20030204@10:06:48 AM at page: http://www.piclist.com/techref/microchip/language/c/S628.htm RH-planet-b Rob Hamerling Code:
// ---------------------------------------------------------------------
// S628.c     Study/experiment with interrupt driven serial
//            communications with a PIC-equiped device as DCE.
//
// Author:    Rob Hamerling.
// Date:      January 2003.
// E-mail:    r.hamerling@hccnet.nl
// homepage:  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).
//  - 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     115200                     // 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                 // (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 (CTSinv == FALSE) {                    // CTS is 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'
          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) & (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 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
    getoffset = (getoffset + 1) & (RCVBUFSIZE - 1);   // update offset
    }

  if (CTSinv == TRUE) {                         // (CTS is 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
      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 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 to show not dead
//
// -------------------------------------------------------------------
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
    clrwdt();                                   // clear before sleep
    sleep();                                    // wait for RB0 or Watchdog
    RTSled = TRUE;                              // RTS LED on
    OPTION = 0b0000.1001;                       // WDT postscaler 1:2
    clrwdt();                                   // clear before sleep
    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

  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

    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
      }

    }

  }

ON 20030204@11:09:51 AM at page: http://www.piclist.com/techref/microchip/language/c/S628.htm RH-planet-b Rob Hamerling edited the page. Difference: http://www.piclist.com/techref/diff.asp?url=H:\techref\microchip\language\c\S628.htm&version=1 ON 20030204@11:10:16 AM at page: http://www.piclist.com/techref/microchip/language/c/S628.htm RH-planet-b Rob Hamerling edited the page. Difference: http://www.piclist.com/techref/diff.asp?url=H:\techref\microchip\language\c\S628.htm&version=2 ON 20030206@12:53:29 PM at page: http://www.piclist.com/techref/microchip/language/c/S628.htm RH-planet-b Rob Hamerling edited the page. Difference: http://www.piclist.com/techref/diff.asp?url=H:\techref\microchip\language\c\S628.htm&version=3 ON 20030206@1:01:53 PM at page: http://www.piclist.com/techref/microchip/language/c/S628.htm RH-planet-b Rob Hamerling edited the page. Difference: http://www.piclist.com/techref/diff.asp?url=H:\techref\microchip\language\c\S628.htm&version=4 ON 20030206@1:04:48 PM at page: http://www.piclist.com/techref/microchip/language/c/S628.htm RH-planet-b Rob Hamerling edited the page. Difference: http://www.piclist.com/techref/diff.asp?url=H:\techref\microchip\language\c\S628.htm&version=5 ON 20030206@1:07:10 PM at page: http://www.piclist.com/techref/microchip/language/c/S628.htm RH-planet-b Rob Hamerling edited the page. Difference: http://www.piclist.com/techref/diff.asp?url=H:\techref\microchip\language\c\S628.htm&version=6