#include "uart.h"

void uartDeInit(void){
#ifdef UART_TX_ENABLE
    TX1STA=0;           //reset    
    anselbits.UART_TX_PIN=0;
    trisbits.UART_TX_PIN=1;
    wpubits.UART_TX_PIN=1;      //pulled up
#endif //UART_TX_ENABLE
    RC1STA=0;           //reset, clear SPEN
#ifdef UART_RX_ENABLE
    RC1IE=0;        //no interrupts
    anselbits.UART_RX_PIN=0;
    trisbits.UART_RX_PIN=1;
    wpubits.UART_RX_PIN=1;      //pulled up
#endif //UART_RX_ENABLE
}

void uartInit(unsigned int b){  //b=baud rate constant
    unsigned char iflag;
    //PPS: UART_TX_PIN and UART_RX_PIN
    iflag=INTCON;               //save interrupt flags
    INTCONbits.GIE=0;           //disable interrupts globally
    PPSLOCK=0x55;
    PPSLOCK=0xAA;
    PPSLOCKED=0;
#ifdef UART_TX_ENABLE
    pps.UART_TX_PIN=0x13;   //TX1
#endif //UART_TX_ENABLE
#ifdef UART_RX_ENABLE
    RX1PPS=UART_RX_PIN;     //RX1
#endif //UART_RX_ENABLE
    PPSLOCKED=1;
    INTCON=iflag;               //restore interrupts    
    //TX
#ifdef UART_TX_ENABLE
    anselbits.UART_TX_PIN=0;
    trisbits.UART_TX_PIN=0;
    wpubits.UART_TX_PIN=0;      //pullup off    
    tx_head=0;
    tx_tail=0;         
    TX1STA=0;           //reset, async mode
    TX1STAbits.TXEN=1;  //TX on
    //TXIE is set when there is data in buffer
#endif //UART_TX_ENABLE
    //BAUD/COMMON
    BAUD1CON=8;         //16bit BRG, low speed
    TX1STAbits.BRGH=1;  //high speed
    SP1BRG=b;           //8e6/b=>baudrate assuming 8MHz
#ifdef UART_RX_ENABLE
    //RX
    anselbits.UART_RX_PIN=0;
    trisbits.UART_RX_PIN=1;
    wpubits.UART_RX_PIN=1;
    rx_head=0;
    rx_tail=0;
    RC1STA=0;           //reset
    RC1STAbits.CREN=1;  //enable receive
    //INTERRUPTS
    RC1IE=1;
#endif //UART_RX_ENABLE
    PEIE=1;         //allow interrupts, GIE set in main
    RC1STAbits.SPEN=1;  //enable serial port
}

#ifdef UART_TX_ENABLE
void uartSend(char c){      //add to txbuf
    //implementation could possibly write directly to output, but not tested working
    while(((tx_head+1)%BUFSIZE)==tx_tail){} //blocking till buffer free
    TX1IE=0;                                //disable while we add to avoid buffer corruption
    txbuf[tx_head]=c;                       //add to queue
    tx_head=(tx_head+1)%BUFSIZE;            //increment pointer
    TX1IE=1;                                //enable interrupts again (or any time there is data)
}

void uartSends(char* c){    //char array
    while(*c){
        uartSend(*c);
        c++;
    }
}
#endif //UART_TX_ENABLE

#ifdef UART_RX_ENABLE
unsigned char uartAvailable(void){   //bytes to receive in rxbuf
    return (unsigned char)((rx_head+BUFSIZE-rx_tail)%BUFSIZE);    
}

char uartReceive(void){     //take from rxbuf
  char c;
  if(rx_head==rx_tail){
      return 0xFF;    //empty
  }
  c=rxbuf[rx_tail];
  rx_tail=(rx_tail+1)%BUFSIZE;
  return c;    
}
#endif //UART_RX_ENABLE

void uartISR(void){
#ifdef UART_RX_ENABLE
    char d;
    if(RC1IF && RC1IE){
        if(RC1STAbits.OERR){RC1STAbits.CREN=0;RC1STAbits.CREN=1;}   //reset overflow error (serious, but better than locking up)
        RC1IF=0;           //clear flag
        d=RC1REG;        //do read
        if(((rx_head+1)%BUFSIZE)!=rx_tail){ //avoid overflow by dropping
            rxbuf[rx_head]=d;
            rx_head=(rx_head+1)%BUFSIZE;
        }
    }
#endif //UART_RX_ENABLE
#ifdef UART_TX_ENABLE
    if(TX1IF && TX1IE){
       if(tx_head!=tx_tail){    //data in buffer
           TX1REG=txbuf[tx_tail];
           tx_tail=(tx_tail+1)%BUFSIZE;
       }else{
           TX1IE=0;              //no more data, shut down
       }
    }    
#endif //UART_TX_ENABLE
}