//basic PIC32 UART implementation
#include <xc.h>
#include "uart.h"
#include <sys/attribs.h>

int uart_init(unsigned long baud){  //assume 8N1 for now
    uart_tx_buffer_head=0;      //reset buffers
    uart_tx_buffer_tail=0;
    uart_rx_buffer_head=0;
    uart_rx_buffer_tail=0;
    U1MODE=0;                   //reset UART
    U1RXRbits.U1RXR=2;        //RPA4 (pin12) for RX
    //ANSELAbits.ANSA4=0;       //if analog pin
    RPB4Rbits.RPB4R=1;        //RPB4 (pin 11) for TX
    //ANSELBbits.ANSB4=0;       if analog pin
    U1MODEbits.BRGH=0;          //standard speed
    U1MODEbits.PDSEL=0;          //8N
    U1MODEbits.STSEL=0;          //1 stop
    U1STA=0;                     //reset
    U1STAbits.URXEN=1;           //enable RX pin
    U1STAbits.UTXEN=1;           //enable TX pin
    //U1BRG=((2500000UL+baud/2)/baud)-1;    //16 bit value, assume FPb of 40MHz
    U1BRG=((3000000UL+baud/2)/baud)-1;    //16 bit value, assume FPb of 48MHz
    //set up interrupts
    __builtin_disable_interrupts();
    U1STAbits.URXISEL=0;        //int on anything received
    U1STAbits.UTXISEL=0;        //int on space available
    IEC1bits.U1RXIE = 1;
    IEC1bits.U1TXIE = 0;        //don't need to turn on till data is queued
    IPC8bits.U1IP=4;            //set priority
    IFS1bits.U1RXIF=0;
    IFS1bits.U1TXIF=0;          //clear flags            
    INTCONbits.MVEC=1;          //multi mode
    while(U1STAbits.URXDA){U1RXREG;}    //empty receive buffer
    __builtin_enable_interrupts();    
    U1MODEbits.ON=1;             //turn on
    return U1BRG;                //to check
}

int uart_send(int b){               //use arduino form
    if(uart_tx_available()||U1STAbits.UTXBF){          //hardware busy, queue in software                    
        while(((uart_tx_buffer_head+1)%UART_BUFFER_SIZE)==uart_tx_buffer_tail){} //blocking till buffer free
        IEC1bits.U1TXIE = 0;        //disable while we add
        uart_tx_buffer[uart_tx_buffer_head]=b;                          //add to queue
        uart_tx_buffer_head=(uart_tx_buffer_head+1)%UART_BUFFER_SIZE;
        IEC1bits.U1TXIE = 1;        //re-enable
    }else{                          //send straight to hardware
        U1TXREG=b;                  //write byte            
    }
    return 1;
}

int uart_print(char *c){             //send char array
    int n=0;
    while(*c){
        uart_send(*c++);
        n++;
    }
    return n;                       //characters written
}
    
int uart_receive(){                  //returns -1 on no data
  int c;
  if(uart_rx_buffer_head==uart_rx_buffer_tail){
      return -1;    //empty
  }
  c=uart_rx_buffer[uart_rx_buffer_tail];
  uart_rx_buffer_tail=(uart_rx_buffer_tail+1)%UART_BUFFER_SIZE;
  return c;
}

int uart_available(){                //    
    return (uart_rx_buffer_head+UART_BUFFER_SIZE-uart_rx_buffer_tail)%UART_BUFFER_SIZE;
}

int uart_tx_available(){              //  bytes queued in tx 
    return (uart_tx_buffer_head+UART_BUFFER_SIZE-uart_tx_buffer_tail)%UART_BUFFER_SIZE;
}

int uart_printi(int n){               //print an int (-32768?)
    int d=10000;
    char zf=0,bytes=0;
    if(n==0){
        uart_send('0');
        return 1;
    }
    if(n<0){
        n=-n;
        uart_send('-');
        bytes++;
    }        
    while(d){
        if((n>(d-1))||zf){
            zf=1;
            uart_send(((n/d)%10)+'0');
            bytes++;
        }
        d=d/10;
    }
    return bytes;
}

int uart_printl(long n){               //print a long
    long d=1000000000;
    char zf=0,bytes=0;
    if(n==0){
        uart_send('0');
        return 1;
    }
    if(n<0){
        n=-n;
        uart_send('-');
        bytes++;
    }        
    while(d){
        if((n>(d-1))||zf){
            zf=1;
            uart_send(((n/d)%10)+'0');
            bytes++;
        }
        d=d/10;
    }
    return bytes;
}

void inttochar(long n, char *c){
    int ptr=0;
    long d=1000000000;
    int zf=0;
    if(n<0){n=-n;c[ptr++]='-';}
    if(n==0){c[0]='0';c[1]=0;return;}
    while(d){
        if((n>(d-1))||zf){
            zf=1;
            c[ptr++]=((n/d)%10)+'0';
        }
        d=d/10;
    }
    c[ptr]=0;   //terminate
}

void __ISR(_UART_1_VECTOR, IPL4SOFT) UART1Handler(void){
    char b;
    if(IFS1bits.U1RXIF){        //rx handler
        while(U1STAbits.URXDA){     //data available
            b=U1RXREG;              //need to read it to clear flag
            if(((uart_rx_buffer_head+1)%UART_BUFFER_SIZE)!=uart_rx_buffer_tail){
                uart_rx_buffer[uart_rx_buffer_head]=b;  
                uart_rx_buffer_head=(uart_rx_buffer_head+1)%UART_BUFFER_SIZE;
            }
        }
        if(U1STAbits.OERR){U1STAbits.OERR=0;}       //clear any overflow error
        IFS1bits.U1RXIF=0;  //clear flag
    }
    if(IFS1bits.U1TXIF){        //tx handler
        while(uart_tx_available()&&(U1STAbits.UTXBF==0)){       //while data and space available
            U1TXREG=uart_tx_buffer[uart_tx_buffer_tail];
            uart_tx_buffer_tail=(uart_tx_buffer_tail+1)%UART_BUFFER_SIZE;
        }
        if(uart_tx_available()==0){IEC1bits.U1TXIE=0;}        //buffer empty, turn off tx
        IFS1bits.U1TXIF=0;  //clear flag
    }
}