/*
 Interrupt driven serial i/o routines.
 P. McGill 26May11
 Based on code by Shane Tolmie and Regulus Berdin at
 www.microchipc.com
*/

#define _INTSERIAL_C_
#include <pic.h>
#include "intserial.h"

unsigned char rxfifo[SER_BUFFER_SIZE];
volatile unsigned char rxinptr, rxoutptr;
bank1 unsigned char txfifo[SER_BUFFER_SIZE];
volatile unsigned char txinptr, txoutptr;
unsigned char ser_tmp;

// Tests to see if any chars are in the receive buffer
bit kbhit(void)
{
    unsigned char dummy;

    if(OERR) {          // if hardware overrun error
        TXEN = 0;       // clear error
        TXEN = 1;
        CREN = 0;
        CREN = 1;
        return 0;
    } // end if
    if(FERR) {          // if hardware framing error
        dummy = RCREG;  // read bogus char
        TXEN = 0;       // clear error
        TXEN = 1;
        return 0;
    } // end if
    return (rxinptr != rxoutptr);
} // end kbhit()


// Gets a char from receive buffer. Will wait for char if buffer is empty.
unsigned char getch(void)
{
    unsigned char c;

    while (kbhit() == 0)        // wait until buffer has a char
        CLRWDT();               // clear the watchdog timer while we wait
    GIE = 0;                    // disable all interrupts
    c = rxfifo[rxoutptr];       // get a char from receive buffer
    ++rxoutptr;                 // bump buffer pointer
    rxoutptr &= SER_FIFO_MASK;  // wrap around if needed
    GIE = 1;                    // enable all interrupts
    return c;
} // end getch()


// Gets a char from the receive buffer and echos it to the sender
unsigned char getche(void)
{
    unsigned char c;

    c = getch();
    putch(c);
    return c;
} // end getche()


// Puts a char into transmit buffer. Will wait if transmit buffer is full.
void putch(unsigned char c)
{
    while (((txinptr + 1) & SER_FIFO_MASK) == txoutptr) // wait until room in xmit buffer
        continue;
    GIE = 0;                    // disable all interrupts
    txfifo[txinptr] = c;        // put char into xmit buffer
    txinptr = (txinptr + 1) & SER_FIFO_MASK;    // bump buffer pointer
    TXIE = 1;                   // enable transmit interrupts
    GIE = 1;                    // enable all interrupts
} // end putch()


// Sends a constant string
void putst(const unsigned char *s)
{
    while(*s)
	putch(*s++);
} // end putst()


// Sends a string
void putst2(unsigned char *s)
{
    while(*s)
	putch(*s++);
} // end putst2


// Sends a char in hexidecimal format
void putchhex(unsigned char v)
{
    unsigned char c;

    c = v >> 4;
    if(c > 9) {
        putch('A' - 10 + c);
    } else {
        putch('0' + c);
    } // end if

    c = v & 0x0F;
    if(c > 9) {
        putch('A' - 10 + c);
    } else {
        putch('0' + c);
    } // end if
} // end putchhex()


void serial_setup(void)
{

    /* relates crystal freq to baud rate - see PIC16F87x data sheet under 'USART async. modes'

    BRGH=1, Fosc=3.6864MHz  BRGH=1, Fosc=4MHz   BRGH=1, Fosc=8MHz   BRGH=1, Fosc=16MHz  BRGH=1, Fosc=20MHz
    ----------------------  -----------------   -----------------   ------------------  ------------------
    Baud        SPBRG       Baud    SPBRG       Baud    SPBRG       Baud    SPBRG       Baud    SPBRG
    1200        191         1200    207.3       1200    415.7       9600    103         9600    129
    2400        95          2400    103.2       2400    207.3       19200   51          19200   64
    4800        47          4800    51.1        4800    103.2       38400   25          38400   31
    9600        23          9600    25.0        9600    51.1        57600   16          57600   20
    19200       11          19200   12.0        19200   25.0        115200  8           115200  10
    38400       5           38400   5.5         38400   12.0
    57600       3           57600   3.3         57600   7.7
    115200      1           115200  1.2         115200  3.3

    */

    #define PIC_CLK 4000000     // enter crystal freq here
    #define BAUD 9600           // and bit rate here
    #define DIVIDER ((PIC_CLK / (16UL * BAUD)) - 1)
    #define HIGH_SPEED 1


    SPBRG = DIVIDER;    // baud rate generator
    BRGH = HIGH_SPEED;	// data rate for sending
    SYNC = 0;		// asynchronous
    SPEN = 1;		// enable serial port pins
    CREN = 1;		// enable reception
    SREN = 0;		// no effect
    TXIE = 0;		// disable tx interrupts until actually transmitting
    RCIE = 1;		// enable rx interrupts
    TX9 = 0;		// 8-bit transmission
    RX9 = 0;		// 8-bit reception
    TXEN = 0;		// reset transmitter
    TXEN = 1;		// enable the transmitter
    PEIE = 1;           // enable peripheral interrupts

    rxinptr = rxoutptr = txinptr = txoutptr = 0;    // zero all fifo pointers

} // end serial_setup()
