//
// UART control module for PIC32MX  (C)Kunihiro Shibuya 2012
//

#include <stdint.h>
#include "global.h"

//******************************************************************************
// Constants
//******************************************************************************

#define	USE_RX_HW_FLOWCTL	UART1_HW_FLOW
#define	USE_TX_HW_FLOWCTL	UART1_HW_FLOW
#define	TX_POLARITY			UART1_TX_POLARITY
#define	RX_POLARITY			UART1_RX_POLARITY

#define	UARTPARAM			UARTPARAM_N81

#define	RXBUFSIZ			128
#define	TXBUFSIZ			128
static volatile char		rxbuf[RXBUFSIZ];
static volatile WORD		rxbufrp,rxbufwp,rxbufcnt;
static volatile char		txbuf[TXBUFSIZ];
static volatile WORD		txbufrp,txbufwp,txbufcnt;
#if	USE_RX_HW_FLOWCTL
#define	FLOWSTOP_RXBUF_SIZ		100
#define	FLOWRESTART_RXBUF_SIZ	80
#define	rts_on()			u1rts_on()	//ON:rx ready
#define	rts_off()			u1rts_off()	//OFF:rx not ready
static volatile char		rxflowstop;
#endif

#define	isRXINT()			(IFS0bits.U1RXIF)
#define	isTXINT()			(IFS0bits.U1TXIF)
#define	RXINT_Clear()		(IFS0bits.U1RXIF=0)
#define	TXINT_Clear()		(IFS0bits.U1TXIF=0)
#define	isEnableRXINT()		(IEC0bits.U1RXIE)
#define	isEnableTXINT()		(IEC0bits.U1TXIE)
#define	EnableRXINT()		(IEC0bits.U1RXIE=1)
#define	EnableTXINT()		(IEC0bits.U1TXIE=1)
#define	DisableRXINT()		(IEC0bits.U1RXIE=0)
#define	DisableTXINT()		(IEC0bits.U1TXIE=0)

#define	UBRG				U1BRG
#define	UMODE				U1MODE
#define	UMODEbits			U1MODEbits
#define	USTA				U1STA
#define	USTAbits			U1STAbits
#define	URXREG				U1RXREG
#define	UTXREG				U1TXREG
#define	ISR_VECTOR			_UART1_VECTOR
#define	ISR_IPL				ipl6
#define	ISR_IPL_NUM			6
#define	SetUARTIPL(x)		(IPC6bits.U1IP=(x),IPC6bits.U1IS=0)
#define	ISR_FUNC			isr_uart ## ISR_VECTOR

#define	BAUDRATEREG(x)		((GetPeripheralClock()/(4*baudrate))-1)


void __attribute__((vector(ISR_VECTOR), interrupt(ISR_IPL), nomips16)) ISR_FUNC(void)
{
	BYTE	dummy;

	if(isRXINT()){
#if	USE_RX_HW_FLOWCTL
		if(!rxflowstop){
			if(rxbufcnt>=FLOWSTOP_RXBUF_SIZ){
				rxflowstop=1;
				rts_off();
			}
		}
#endif
		if(rxbufcnt<RXBUFSIZ){
			rxbuf[rxbufwp]=URXREG;
			rxbufwp=(rxbufwp+1)&(RXBUFSIZ-1);
			rxbufcnt++;
		}else{
			//バッファフル→読み捨て
			dummy=URXREG;
		}
		RXINT_Clear();
	}

	if(isTXINT()){
		if(txbufcnt){
			UTXREG=txbuf[txbufrp];
			txbufrp=(txbufrp+1)&(TXBUFSIZ-1);
			txbufcnt--;
		}else{
			DisableTXINT();
			return;
		}
		TXINT_Clear();
	}
}

void UART1Init(unsigned long baudrate)
{
	DisableRXINT();
	DisableTXINT();

    UBRG = BAUDRATEREG(baudrate);
    UMODE = 0;
    UMODEbits.BRGH = 1;
	UMODEbits.PDSEL = (UARTPARAM&UARTPARAM_PDSEL_MASK)>>UARTPARAM_PDSEL_SHIFT;
	UMODEbits.STSEL = (UARTPARAM&UARTPARAM_STSEL_MASK)>>UARTPARAM_STSEL_SHIFT;
#if	USE_TX_HW_FLOWCTL
	UMODEbits.UEN = 2;
#endif
#if	RX_POLARITY
	UMODEbits.RXINV = 1;
#endif
	//UMODEbits.WAKE=1;
    UMODEbits.UARTEN = 1;
    USTA = 0;
#if	TX_POLARITY
	USTAbits.UTXINV = 1;
#endif
	USTAbits.UTXEN = 1;
    RXINT_Clear();
    
    #if defined (__PIC32MX__)
        USTAbits.URXEN = 1;
    #endif

	rxbufrp=rxbufwp=rxbufcnt=0;
	txbufrp=txbufwp=txbufcnt=0;
#if	USE_RX_HW_FLOWCTL
	rxflowstop=0;
	rts_on();
#endif
	SetUARTIPL(ISR_IPL_NUM);
	EnableRXINT();
}

#if	UARTPARAM&(UARTPARAM_PARGEN_EVEN|UARTPARAM_PARGEN_ODD)
static int getparity_even(int ch)
{
	unsigned	i,mask,bitcount;

	mask=1;
	bitcount=0;
	for(i=0;i<7;i++,mask<<=1){
		if(ch&mask){
			bitcount++;
		}
	}
	return bitcount&1;
}
#endif

void UART1PutChar( char ch )
{
    // ? TX バッファに空きができるまで待つ
	while(txbufcnt>=TXBUFSIZ);

    // ↓ 割り込みと競合しないよう保護
    DisableTXINT();    
    
#if	UARTPARAM&UARTPARAM_PARGEN_EVEN
	txbuf[txbufwp]=(char)(((int)ch&0x7f) + (getparity_even(ch)?0x80:0x00));
#elif	UARTPARAM&UARTPARAM_PARGEN_ODD
	txbuf[txbufwp]=(char)(((int)ch&0x7f) + (getparity_even(ch)?0x00:0x80));
#else
	txbuf[txbufwp]=ch;
#endif
	txbufwp=(txbufwp+1)&(TXBUFSIZ-1);

	txbufcnt++;
	EnableTXINT();
    
    // ? 送信済み（TXBUF 空）まで待つ
    //    (ハードウェア FIFO が空になるまで)
    while (txbufcnt > 0);

    // ? UART ハードウェアの送信完了を待つ
    //    (シフトレジスタ送信完了)
    while (!USTAbits.TRMT);
}

int UART1GetChar()
{
	int	ret=-1;

	if(USTAbits.OERR){
		USTAbits.OERR=0;
	}

	if(rxbufcnt){
		ret=((BYTE)rxbuf[rxbufrp]);
#if	UARTPARAM&(UARTPARAM_PARGEN_EVEN|UARTPARAM_PARGEN_ODD)
		ret&=0x7f;
#endif
		rxbufrp=(rxbufrp+1)&(RXBUFSIZ-1);
		DisableRXINT();
		rxbufcnt--;
#if	USE_RX_HW_FLOWCTL
		if(rxflowstop){
			if(rxbufcnt<FLOWRESTART_RXBUF_SIZ){
				rxflowstop=0;
				rts_on();
			}
		}
#endif
		EnableRXINT();
//UART1PutChar(ret);
	}

	return ret;
}

// ワード(2バイト)単位でのシリアル通信ラッパ
// ビッグエンディアン(0x1234は0x12 0x34の順番で送受信する)

// バッファに溜まっているデータの数をワード単位で得る
// データが来るまで待機し、1ワード受信する
unsigned int serialReadOneWordBlock(void) {
    uint32_t Cnt;
    uint8_t buf[2];
    
    Cnt=0;
    do {
        if (Cnt==0) {
            while(rxbufcnt==0);
            buf[0]=UART1GetChar();
            Cnt=1;
        }
        if (Cnt==1) {
            while(!(U1STA & _U1STA_URXDA_MASK));
            buf[1]=UART1GetChar();
            Cnt=2;
        }
    } while(Cnt < 2);
    
    return (((unsigned int)buf[0])<<8)|((unsigned int)buf[1]);}

// 1ワード送信する
void serialWriteOneWord(unsigned int w) {
    UART1PutChar((w>>8)&0xff);
    UART1PutChar(w&0xff);
}

