//============================================================================================
//      tc_sci_isr.c          ver 2.2   (7/22/08)
//
//      copyright (c) 2001-2008 Vaux Electronics, Inc.
//============================================================================================


//============================================================================================
//
//  This 16-bit version requires 16-bit pointers enabled (#device *=16) so compiler can access
//  all of ram.  This is flexible, and allows different-sized buffers to be allocated, letting
//  the compiler place buffers in ram as needed.
//
//  Targeting an 18F2620 or 18F252, we default to a 250-byte RX buffer, and a 250-byte TX buffer.
//  For 18F2620/252, can increase rx buffer to 500 bytes.
//  For 16F876, should use an 80-byte RX buffer, and a 48-byte TX buffer.
//
//  Serial i/o for the sci (usart) is handled in this file, which provides buffered i/o
//  in TBE_isr() and RDA_isr(), as well as interface routines for the main program.
//
//  Public routine print_byte(value) prints an 8-bit integer as 000 to 255
//
//  Public routine print_word(value) prints a 16-bit integer as 00000 to 65535
//
//  Public routine send_sci_tx_byte(c) allows main loop to send byte to sci (usart)
//  by adding to the transmit buffer, and enabling the transmit interrupt.
//  If buffer is full, it waits for it to empty first.
//
//  Public routine c = get_sci_rx_byte() allows main loop to get next byte from sci (usart)
//  receive buffer.
//  Returns 0 if the buffer is empty.
//  If buffer was full, we lost byte(s).  This will get flagged since isr will load
//  diag_code with DIAG_SCI_INBUF_FULL, so main is aware of it (send error mssg).
//  We pass all CR and LF chars -- note that host computer's new-line char(s) can vary:
//      - windows/dos systems use CR/LF (\r\n)
//      - unix systems use LF (\n)
//      - macintosh systems use CR (\r)

//
//  Interrupt routine TBE_isr() is called when TBE interrupts are enabled, and the
//  sci (usart) transmit register is empty.  It gets next byte (if any) from circular stx_buf
//  and sends to sci.
//
//  Interrupt routine void RDA_isr() is called when RDA interrupts are enabled, and the
//  sci (usart) receive register has a byte.  It gets byte from sci and puts into circular
//  srx_buf (if space avail).
//
//      In main prog:
//
//          #device   *=16                          // 16-bit pointers for full ram
//            Note:  if *=16 is not used, 8-bit pointers will be more efficient, but can only
//            address 256 bytes of ram -- read_bank() and write_bank() commands may then be
//            used to address the upper banks, and get faster access (see ex_psp.c)
//            Not using bank access for now, just using 16-bit pointers.
//          #define         SCI_RX_XOFF   PIN_C5    // optional: HW handshake line (or monitor xon/xoff)
//          #define         SCI_TX        PIN_C6    // SCI: TX line
//          #define         SCI_RX        PIN_C7    // SCI: RX line
//          #USE RS232 (BAUD=38400, XMIT=SCI_TX, RCV=SCI_RX, BITS=8, PARITY=N, ERRORS)
//            Note: If ENABLE is used (for 485 chip), then putc will wait for entire duration of
//            byte output in the TBE_isr -- removing ENABLE allows fast tranfer to tx register.
//          //#use RS232 (BAUD=38400, XMIT=SCI_TX, RCV=SCI_RX, ENABLE=PIN_xx, BITS=8, PARITY=N, ERRORS)
//          #use STANDARD_IO (C)
//          // or:  #use FIXED_IO   (C_OUTPUTS=SCI_RX_XOFF)
//          ...
//          #priority TIMER1, RDA, TBE...           // int priorities
//          #include  "vaux_std.h"                  // TRUE, FALSE, XON...
//
//          #define   SCI_TXBUF_SIZE       250      // define before sci_16bit_isr.c to change
//          #define   SCI_TXBUF_HIGH       200      // "
//          #define   SCI_RXBUF_SIZE       500      // "
//          #define   SCI_RXBUF_HIGH       450      // "
//          #define   SCI_RXBUF_LOW         20      // "
//          #include  "tc_sci_isr.c"                // interrupt routines for sci (usart) i/o
//          ...
//          void main()
//          {
//            ...
//            #ifdef SCI_RX_XOFF
//              output_low (SCI_RX_XOFF);           // init
//            #endif
//
//            use_XONXOFF_flg = ON;                 // ON to enable SW handshaking
//
//            enable_interrupts(INT_RDA);           // sci receive int
//            enable_interrupts(GLOBAL);
//            ...
//            c = get_sci_rx_byte();                // get sci rx char
//            if (c)
//              (do something, else no char avail)
//            ...
//            send_sci_tx_byte(c);                  // send sci tx char
//            ...
//          }
//
//============================================================================================
//
//  Data Memory banking:
//
//          PIC         B0    B1    B2    B3    B4    B5     Total
//      ----------      --    --    --    --    --    --      ---
//      16F876/877      96    80    96    96     0     0      368
//      18F252/452     256   256   256   256   256   256     1536
//      18F2620/4620   (15.5 banks of 256..............)     3968
//
//============================================================================================
//
//      to do:
//          - add extra xonxoff stuff added in lattis 7.1.0.0?
//
//      tc_sci_isr.c          ver 2.2   (7/22/08)
//          - comments only
//
//      tc_sci_isr.c          ver 2.0   (6/20/06)
//          - was named tc_sci_16bit_isr.c
//          - srx_buf_count is now int16 (was int8) (for buffer > 255)
//          - stx_buf_count is now int16 (was int8) (in case we want to increase tx buffer past 255 bytes)
//          - replaced short (was typedef) with int1
//          - replaced byte (was typedef) with int8
//          - replaced word (was typedef) with int16
//
//      tc_sci_16bit_isr.c    ver 1.0   (9/8/04)
//          - created from lattis sci_16bit_isr.c code, with changes:
//              - removed all char filtering from RDA_isr()
//              - in RDA_isr(), if the receive buffer overflows, we set diag_code = DIAG_SCI_INBUF_FULL
//
//============================================================================================


//  ----- prototypes:
void print_byte(int8 value);                        // prints an 8-bit integer as 000 to 255
void print_word(int16 value);                        // prints a 16-bit integer as 00000 to 65535

void send_sci_tx_byte(int8 c);                      // send byte to sci (usart) tx buffer
int8 get_sci_rx_byte(void);                         // get next byte from sci (usart) rx buffer

#int_TBE
void TBE_isr(void);                                 // sci (usart) serial transmit data isr
#int_RDA
void RDA_isr(void);                                 // sci (usart) serial receive data isr


// ----- defines:
#ifndef SCI_TXBUF_SIZE
  #define     SCI_TXBUF_SIZE              250
#endif

#ifndef SCI_TXBUF_HIGH
  #define     SCI_TXBUF_HIGH              200
#endif

#ifndef SCI_RXBUF_SIZE
  #define     SCI_RXBUF_SIZE              250
#endif

#ifndef SCI_RXBUF_HIGH
  #define     SCI_RXBUF_HIGH              200
#endif

#ifndef SCI_RXBUF_LOW
  #define     SCI_RXBUF_LOW               20
#endif


// ----- globals:
int1   use_XONXOFF_flg = OFF;                       // set by main to enable SW handshaking

int1   sci_rx_paused_flg = OFF;                     // set when xoff sent to host to stop rx
int1   sci_tx_paused_flg = OFF;                     // set when xoff received from host
                                                    //    (not yet using xonxoff on tx side)

int8   stx_buf[SCI_TXBUF_SIZE];                     // stx_buf is circular tx buffer for sci

int8   *stx_buf_wr = stx_buf;                       // write pointer for stx_buf
int8   *stx_buf_rd = stx_buf;                       // read pointer for stx_buf

int16  stx_buf_count = 0;                           // number of bytes in the stx_buf
int8   stx_special_byte = 0;                        // priority tx byte (eg: xon/xoff)

int8   srx_buf[SCI_RXBUF_SIZE];                     // srx_buf is circular rx buffer for sci

int8   *srx_buf_wr = srx_buf;                       // write pointer for srx_buf
int8   *srx_buf_rd = srx_buf;                       // read pointer for srx_buf

int16  srx_buf_count = 0;                           // number of bytes in the srx_buf

int8   rda_tmp_byte = 0;                            // temp bytes in rda int routine
                                                    //  (global just in case...)



//--------------------------------------------------------------------------------------------
//  void print_byte(int8 value)                     prints an 8-bit integer as 000 to 255
//
//  Public routine print_byte(value) prints an 8-bit integer as 000 to 255

void print_byte(int8 value)
{
  int8 i, digit, divisor;

  divisor = 100;
  for (i=0; i<3; i++)
  {
    digit = value / divisor;
    value -= digit * divisor;
    divisor /= 10;
    send_sci_tx_byte (digit + '0');
  }
}


//--------------------------------------------------------------------------------------------
//  void print_word(int16 value)                     prints a 16-bit integer as 00000 to 65535
//
//  Public routine print_word(value) prints a 16-bit integer as 00000 to 65535

void print_word(int16 value)
{
  int8 i, digit;
  int16 divisor;

  divisor = 10000;
  for (i=0; i<5; i++)
  {
    digit = (int8)(value / divisor);
    value -= (int16)(digit) * divisor;
    divisor /= 10;
    send_sci_tx_byte (digit + '0');
  }
}


//--------------------------------------------------------------------------------------------
//  void send_sci_tx_byte(int8 c)                   send byte to sci (usart) tx buffer
//
//  Public routine send_sci_tx_byte(c) allows main loop to send byte to sci (usart)
//  by adding to the transmit buffer, and enabling the transmit interrupt.
//  If buffer is full, it waits for it to empty first.
//
//  PIC will automatically call the TBE_isr function and send the byte.
//  We check for a full buffer and wait here until it empties before adding next char.
//  A full buffer takes about 13 msec to send (48 chars x ~0.26ms/char @ 38400-baud).

void send_sci_tx_byte(int8 c)
{
  if(stx_buf_count == SCI_TXBUF_SIZE)               // if buffer full,
  {
    while(stx_buf_count)                            // just wait until empty
      restart_wdt();
  }

  *stx_buf_wr++ = c;                                // put byte into tx buffer, and inc ptr
  if(stx_buf_wr == (stx_buf + SCI_TXBUF_SIZE))      // did write ptr wrap?
    stx_buf_wr = stx_buf;                           // if so, reset it to start of buffer
  stx_buf_count++;                                  // increment byte count
  enable_interrupts(INT_TBE);                       // turn on tx interrupt
}


//--------------------------------------------------------------------------------------------
//  int8 get_sci_rx_byte(void)                      get next byte from sci (usart) rx buffer
//
//  Public routine c = get_sci_rx_byte() allows main loop to get next byte from sci (usart)
//  receive buffer.
//  Returns 0 if the buffer is empty.
//  If buffer was full, we lost byte(s).  This will get flagged since isr will load
//  diag_code with DIAG_SCI_INBUF_FULL, so main is aware of it (send error mssg).
//  We pass all CR and LF chars -- note that host computer's new-line char(s) can vary:
//      - windows/dos systems use CR/LF (\r\n)
//      - unix systems use LF (\n)
//      - macintosh systems use CR (\r)
//
//  If use_XONXOFF_flg flag is ON, this routine initiates sending of XON char
//  to host when buffer empties (XOFF is handled in get_RDA_isr).
//  Note:   If XON/XOFF works properly, RX buffer should not overflow.
//  If SCI_RX_XOFF pin has been defined (in main), we clear here when XON is sent.
//  This can be used as a hardware handshake line, or to monitor xon/xoff state.

int8 get_sci_rx_byte(void)
{
  int8 c=0;

  // ----- handle XON flow control, if enabled:
  if (use_XONXOFF_flg)
  {
    // --- if rx is currently paused and rx buffer getting empty, send XON:
    if (sci_rx_paused_flg)
    {
      if (srx_buf_count < SCI_RXBUF_LOW)
      {
        #ifdef SCI_RX_XOFF
          output_low (SCI_RX_XOFF);                 // HW handshake line (or monitor xoff)
        #endif
        sci_rx_paused_flg = FALSE;
        stx_special_byte = XON;                     // XON to be transmitted immediately
        enable_interrupts(INT_TBE);                 // turn on tx interrupt to send it next
      }
    }
  }

  // ----- get byte from buffer, if there is one available:
  if(srx_buf_count > 0)                             // check if there is any data
  {
    c = *srx_buf_rd++;                              // read byte from buffer
    if(srx_buf_rd == (srx_buf + SCI_RXBUF_SIZE))    // did read ptr wrap?
      srx_buf_rd = srx_buf;                         // if so, reset it to start of buffer
    srx_buf_count--;                                // decrement byte count
  }
  return(c);
}


//--------------------------------------------------------------------------------------------
//  void TBE_isr(void)                              sci (usart) serial transmit data isr
//
//  Interrupt routine TBE_isr() is called when TBE interrupts are enabled, and the
//  sci (usart) transmit register is empty.  It gets next byte (if any) from circular stx_buf
//  and sends to sci.
//
//  This interrupt must be enabled with:
//    enable_interrupts(GLOBAL);
//
//          Microchip details:
//            The main program writes data to the usart transmit register TXREG
//            (when TXREG is empty).  This byte gets automatically loaded into the
//            transmit shift register TSR (when TSR is empty).  When TXREG is empty
//            (after data moves into TSR), the transmit interrupt flag TXIF is set.
//            This will generate an interrupt if the transmit interrupt enable bit TXIE
//            has been set by the main program, allowing the isr routine to load the
//            next char into TXREG.
//            Note:  TXIF is initially set when TXIE is set, and TXIF is only cleared
//            when data is loaded into TXREG.
//
//  #use RS232 (BAUD=38400, XMIT=PIN_C6, RCV=PIN_C7, BITS=8, PARITY=N, ERRORS)
//  //#use RS232 (BAUD=38400, XMIT=PIN_C6, RCV=PIN_C7, ENABLE=PIN_xx, BITS=8, PARITY=N, ERRORS)
//  Note: If ENABLE is used (for 485 chip), then putc will wait for entire duration of
//  byte output in the TBE_isr -- removing ENABLE allows fast tranfer to tx register.
//
//  If stx_special_byte is non-zero,  it will get priority, and be sent regardless
//  of tx buffer contents -- primary use is to send xon/xoff.
//
//  ??? Should we monitor SCI_TX_Paused_Flg in here to pause/restart tx, or just ignore it?
//  (the state of incoming XON/XOFF chars is held in SCI_TX_Paused_Flg)

#int_TBE
void TBE_isr(void)
{
  #ifdef SCI_INT_MON                                // toggle SCI isr monitor pin
    if (input(SCI_INT_MON))
      output_low (SCI_INT_MON);
    else
      output_high (SCI_INT_MON);
  #endif

  if(stx_special_byte)                              // non-zero special byte (eg: xon/xoff)
  {
    putc(stx_special_byte);                         // special byte has proirity
    stx_special_byte = 0;                           // reset to 0
  }
  else
  {
    if(stx_buf_count != 0)                          // any chars to send?
    {
      putc(*stx_buf_rd++);                          // send next char and increment read ptr
      if(stx_buf_rd == (stx_buf + SCI_TXBUF_SIZE))  // did read ptr wrap?
        stx_buf_rd = stx_buf;                       // if so, reset ptr to start of buffer
      stx_buf_count--;                              // decrement char count
    }
    else
    {
      disable_interrupts(INT_TBE);                  // if no chars, turn off tx interrupt
    }
  }

  #ifdef SCI_INT_MON                                // toggle SCI isr monitor pin
    if (input(SCI_INT_MON))
      output_low (SCI_INT_MON);
    else
      output_high (SCI_INT_MON);
  #endif
}


//--------------------------------------------------------------------------------------------
//  void RDA_isr(void)                              sci (usart) serial receive data isr
//
//  Interrupt routine void RDA_isr() is called when RDA interrupts are enabled, and the
//  sci (usart) receive register has a byte.  It gets byte from sci and puts into circular
//  srx_buf (if space avail).
//
//  This interrupt must be enabled with:
//    enable_interrupts(INT_RDA);
//    enable_interrupts(GLOBAL);
//
//          Microchip details:
//            Receive interrupt (INT_RDA) occurs at end of char reception.  The SCI has a two-
//            byte fifo (RCREG) in addition to a possible third byte in its shift register.
//            When RCREG has one or two bytes available, receive interrupt flag TXIF is set.
//            This will generate an interrupt if the receive interrupt enable bit RCIE
//            has been set by the main program, allowing the isr routine to get the next byte.
//            If the SCI fifo is full (two bytes) when the third byte gets to the stop bit,
//            there will be an overrun error indicated by OERR being set.  OERR must be reset
//            by the program (CCS compiler does this when the RS232 ERRORS parameter is used).
//            Framing error bit FERR is set when a stop bit is invalid (this is also cleared
//            bt CCS compiler ERRORS option in the RS232 definition line).
//
//    The RDA_isr routine is called when there is a byte (or two) in the SCI fifo,
//    and puts the oldest byte into a circular buffer in ram.  If there is a second
//    byte available, the isr will get triggered again.  It is the responsibility of
//    the main program loop (or timer int task) to prevent buffer overflow by removing
//    chars promptly from receive buffer by calling get_sci_rx_byte.
//
//    If use_XONXOFF_flg flag is ON, this routine initiates sending of XOFF char
//    to host when buffer gets full (XON is handled in get_sci_rx_byte).
//    Note:   If XON/XOFF works properly, RX buffer should not overflow.
//    If SCI_RX_XOFF pin has been defined (in main), we set here when XOFF is sent.
//    This can be used as a hardware handshake line, or to monitor xon/xoff state.
//
//    We do not filter incoming chars.  We just add all chars to rx buffer (allowing
//    8-bit data reception).  The get_sci_rx_byte (or subsequently higher) routine
//    that reads the buffer may choose to clear bit-8, filter chars, etc.
//
//    If the receive buffer overflows, we set diag_code = DIAG_SCI_INBUF_FULL.


#INT_RDA
void RDA_isr(void)
{
  #ifdef SCI_INT_MON                                // toggle SCI isr monitor pin
    if (input(SCI_INT_MON))
      output_low (SCI_INT_MON);
    else
      output_high (SCI_INT_MON);
  #endif

  // ----- get byte from sci (usart):
  rda_tmp_byte = getc();

  // ----- handle XOFF flow control, if enabled:
  if (use_XONXOFF_flg)
  {
    // --- if rx not currently paused and rx buffer getting full, send XOFF:
    if (!sci_rx_paused_flg)
    {
      if (srx_buf_count > SCI_RXBUF_HIGH)
      {
        #ifdef SCI_RX_XOFF
          output_high (SCI_RX_XOFF);                // HW handshake line (or monitor xoff)
        #endif
        sci_rx_paused_flg = TRUE;
        stx_special_byte = XOFF;                    // XOFF to be transmitted immediately
        enable_interrupts(INT_TBE);                 // turn on tx interrupt to send it next
      }
    }

    // --- check for incoming xon/xoff, and flag for tx routine:
    if (rda_tmp_byte == XON)
    {
      sci_tx_paused_flg = FALSE;
    }
    if (rda_tmp_byte == XOFF)
    {
      sci_tx_paused_flg = TRUE;
    }
  }


  // ----- filter bytes before adding to rx buffer:
  //  if (use_rx_filtering_flg)
  //  {
  //    if (...)
  //      rda_tmp_byte = 0;
  //  }


  // ----- add byte to rx buffer:
  if (rda_tmp_byte)                                 // rda_tmp_byte filtered out if it's now 0
  {
    if (srx_buf_count < SCI_RXBUF_SIZE)             // any space for a new byte?
    {
      *srx_buf_wr++ = rda_tmp_byte;                 // put byte into rx buffer, and inc ptr
      if (srx_buf_wr == (srx_buf + SCI_RXBUF_SIZE)) // did write ptr wrap?
        srx_buf_wr = srx_buf;                       // if so, reset it to start of buffer
      srx_buf_count++;                              // increment byte count
    }
    else
    {
      diag_code = DIAG_SCI_INBUF_FULL;              // overflow -- update diag_code
    }
  }

  #ifdef SCI_INT_MON                                // toggle SCI isr monitor pin
    if (input(SCI_INT_MON))
      output_low (SCI_INT_MON);
    else
      output_high (SCI_INT_MON);
  #endif
}



