// ****************************************************************************
// INCLUDE FILES: Header files of modules referenced by this module
// ****************************************************************************
#include <string.h>
#include <pdc.h>
#include <usart.h>
#include <sysclk.h>
#include <usart_rs485mode.h>
#include <AssertISTP.h>
#include <ISTPManager.h>


// ****************************************************************************
// PRIVATE TYPES: Private types used by this module only
// ****************************************************************************


// ****************************************************************************
// PRIVATE MACROS: Private #define directives used by this module only
// ****************************************************************************

// Configuration of USART used for ISTP purposes
#define ISTP_USART                 USART1
#define ISTP_USART_ID              ID_USART1
#define ISTP_USART_BAUDRATE        8000000
#define ISTP_USART_CHAR_LENGTH     US_MR_CHRL_8_BIT
#define ISTP_USART_PARITY          US_MR_PAR_NO
#define ISTP_USART_STOP_BIT        US_MR_NBSTOP_1_BIT
#define ISTP_USART_IRQn            USART1_IRQn

#define USART_TX_BUFFER_SIZE (14 * 1024)

// ****************************************************************************
// PRIVATE VARIABLES: Variables used by this module only (static)
// ****************************************************************************
// Buffers used for Rx DMA transfer
static uint8_t rxTransferBuffer1[USART_RX_BUFFER_SIZE];
static uint8_t rxTransferBuffer2[USART_RX_BUFFER_SIZE];
// Buffer pointer for reading data from the Rx DMA buffers
static uint8_t * rxTransferBufferCurrent = NULL;
static uint8_t * rxTransferBufferNext = NULL;
// Counters of received data used for reading the Rx DMA buffers
static uint32_t rxCntr = 0;
static uint32_t rxCntrPrevious = 0;

static uint8_t txTransferBuffer1[USART_TX_BUFFER_SIZE];
static uint8_t txTransferBuffer2[USART_TX_BUFFER_SIZE];
static uint8_t * txTransferBufferActive = txTransferBuffer1;
static uint32_t txTransferBufferWriteIndex = 0;
static uint8_t txTransferBufferOverflow = 0;


// Pointer to PDC register base
static Pdc *g_p_pdc;
// PDC data packet.
static pdc_packet_t g_st_packet;

// ****************************************************************************
// PRIVATE FUNCTIONS PROTOTYPES: Functions used by this module only (static)
// ****************************************************************************
static void USART_resetRxTransfer(void);


// ****************************************************************************
// PUBLIC VARIABLES: Public variables shared with other modules
// ****************************************************************************


// ****************************************************************************
// PRIVATE FUNCTIONS: Private functions used by this module only
// ****************************************************************************
static void USART_resetRxTransfer(void)
{
	// Initialize Rx DMA transfer

	// Clear all the receive buffers
	memset(rxTransferBuffer1, 0x0, USART_RX_BUFFER_SIZE);
	memset(rxTransferBuffer2, 0x0, USART_RX_BUFFER_SIZE);
	
	// Set the pointer to the active buffer
	// rxTransferBuffer1 is set for the current DMA transfer,
	// rxTransferBuffer2 is set for next DMA transfer
	rxTransferBufferCurrent = rxTransferBuffer1;
	rxTransferBufferNext = rxTransferBuffer2;
	// Rx counters of both buffers are at its maximums at the beginning
    rxCntr = USART_RX_BUFFER_SIZE;
	rxCntrPrevious = USART_RX_BUFFER_SIZE;
	
	// Initialize the current DMA transfer
	
	// Set the target address for the received data
	g_st_packet.ul_addr = (uint32_t)rxTransferBufferCurrent;
	// Set the size of the DMA transfer in bytes
	g_st_packet.ul_size = USART_RX_BUFFER_SIZE;
	// Write config to the appropriate registers
	pdc_rx_init(g_p_pdc, &g_st_packet, NULL);
	
	// Initialize the next DMA transfer
	
	// Set the target address for the received data
	g_st_packet.ul_addr = (uint32_t)rxTransferBufferNext;
	// Set the size of the DMA transfer in bytes
	g_st_packet.ul_size = USART_RX_BUFFER_SIZE;
	// Write config to the appropriate registers
	pdc_rx_init(g_p_pdc, NULL , &g_st_packet);
}

// ****************************************************************************
// PUBLIC FUNCTIONS: Public functions shared with other modules
// ****************************************************************************
void USART_Init(void)
{
	const sam_usart_opt_t usart_console_settings = 
	{
		ISTP_USART_BAUDRATE,
		ISTP_USART_CHAR_LENGTH,
		ISTP_USART_PARITY,
		ISTP_USART_STOP_BIT,
		US_MR_CHMODE_NORMAL
	};

	usart_reset(ISTP_USART);

	// Enable the peripheral clock in the PMC
	sysclk_enable_peripheral_clock(ISTP_USART_ID);

    usart_init_hw_handshaking(ISTP_USART, &usart_console_settings,
    sysclk_get_cpu_hz());


	
    // Disable interrupts
	usart_disable_interrupt(ISTP_USART, US_IDR_ENDRX);
	usart_disable_interrupt(ISTP_USART, US_IDR_ENDTX);
	
    // Enable both receiver and transmitter
	usart_enable_tx(ISTP_USART);
	usart_enable_rx(ISTP_USART);
	
	// Get board USART PDC base address (DMA control registers)
	g_p_pdc = usart_get_pdc_base(ISTP_USART);
	
    // Prepare Rx transfer for data reception
	USART_resetRxTransfer();

	// Enable both Rx transfer and Tx transfer even when the Tx transfer is
	// not configured yet. No actual transmission appears when Tx transfer is enabled
	// because Tx transfer size is set to zero after reset of the CPU.
	pdc_enable_transfer(g_p_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN);
}

//******************************************************************************
//! @fn void USART_transmit_buffer(const void* p_buff, uint32_t ulsize)
//!
//! @description
//! 
//!
//! @param    
//!           
//!
//! @return   
//!
//! @calls    
//!           
//!           
//!           
//!           
//!
//! @req
//!
//! @example
//!
//! @code
//!
//! @endcode
//******************************************************************************
void USART_transmit_buffer(const void* p_buff, uint32_t ulsize)
{	
	
    if( (txTransferBufferWriteIndex + ulsize) <= USART_TX_BUFFER_SIZE )
    {
        memcpy( &txTransferBufferActive[txTransferBufferWriteIndex], p_buff, ulsize);
        txTransferBufferWriteIndex += ulsize;
    }
    else
    {
        txTransferBufferOverflow = 1;
        ISTPMSetAlertTxBufferOverflow(1);
    }

    // Tx DMA transfer indicates end, when last byte is transferred to the transmit holding register
    // of the USART periphery.
    // Wait here for Tx DMA transfer end as well as transmit holding and shift register empty, so that
    // USART is fully ready for next data to be transmitted.
    if( usart_is_tx_buf_end(ISTP_USART) && usart_is_tx_empty(ISTP_USART) )
    {
        // Tx DMA transfer is completely done, start next transfer
        // with the buffered data.
        
        // Set the address and size of the data to be transmitted
	    g_st_packet.ul_addr = (uint32_t)txTransferBufferActive;
	    g_st_packet.ul_size = txTransferBufferWriteIndex;
	    
        // Switch the active buffer used for buffering data
        // inside the ISTP platform
        if( txTransferBufferActive == txTransferBuffer1 )
        {
            txTransferBufferActive = txTransferBuffer2;
        }
        else if( txTransferBufferActive == txTransferBuffer2 )
        {
            txTransferBufferActive = txTransferBuffer1;
        }
        else
        {
            // Wrong pointer to the Tx buffer
            ASSERT_ISTP( 0x00, ASSERT_USART_3 );
        }

        // Reset write index, so that the active buffer is used
        // from the beginning
        txTransferBufferWriteIndex = 0;

        // Initialize the Tx DMA transfer from memory to USART transmitter
	    // This is the actual trigger of data transmission
	    pdc_tx_init(g_p_pdc, &g_st_packet, NULL);
    }
}


//******************************************************************************
//! @fn uint32_t USART_receiveData(uint8_t * rxData)
//!
//! @description
//! 
//!
//! @param    
//!           
//!
//! @return   
//!
//! @calls    
//!           
//!           
//!           
//!           
//!
//! @req
//!
//! @example
//!
//! @code
//!
//! @endcode
//******************************************************************************
uint32_t USART_receiveData(uint8_t * rxData)
{
	uint32_t readIndex;
	uint32_t readSize;
	uint32_t helper;
	uint8_t  * temporaryPtr;
	uint32_t rxBytesCount;
		
		
	if( usart_is_rx_buf_full(USART1) )
	{
		// Both receive buffers are full.
		// Buffer overflow occurred
		
		// Copy the rest of the bytes from the rxTransferBufferCurrent which
		// was not read so far
		readIndex = USART_RX_BUFFER_SIZE - rxCntrPrevious;
		readSize = USART_RX_BUFFER_SIZE - readIndex;
		
        if(readSize > 0)
		{
		    memcpy(rxData, &rxTransferBufferCurrent[readIndex], readSize);	
		}
        else
        {
        }

		// Check if last call of this function was reading from the last
		// DMA rx buffer used.
		helper = pdc_read_rx_ptr(g_p_pdc);
		
        if( ( ((uint8_t *)helper) != ( rxTransferBufferCurrent + USART_RX_BUFFER_SIZE ) ))
		{
			// Previous call was reading from the different buffer because
            // the final DMA pointer points to different address than the end
            // of the current buffer.
            // This means that also the second buffer is full and should be read
			
			// Copy all bytes from the rxTransferBufferNext
			memcpy(&rxData[readSize], rxTransferBufferNext, USART_RX_BUFFER_SIZE);
		}
		
		rxBytesCount = readSize + USART_RX_BUFFER_SIZE;  
		
		// Initialize the transfer from the beginning
		USART_resetRxTransfer();
	}
	else
	{
		// Both receive buffers are not full
		// No buffer overflow, no bytes lost
		
		// Check if first buffer is full
		if( usart_is_rx_buf_end(USART1) )
		{
			// First buffer is full

            // Read the receive counter for the buffer currently used by DMA
			// to store the received data
			rxCntr = pdc_read_rx_counter(g_p_pdc);
						
			// rxCntr is valid for the rxTransferBufferNext and rxCntrPrevious is valid
			// for the rxTransferBufferCurrent
			// Copy the rest of the bytes from the rxTransferBufferCurrent
			readIndex = USART_RX_BUFFER_SIZE - rxCntrPrevious;
			readSize = USART_RX_BUFFER_SIZE - readIndex;
			
            if(readSize > 0)
            {
                memcpy(rxData, &rxTransferBufferCurrent[readIndex], readSize);
            }
            else
            {
            }

            helper = readSize;
            			
			// And copy received bytes from the rxTransferBufferNext	
			readIndex = 0;
			readSize = USART_RX_BUFFER_SIZE - rxCntr;
			
            if(readSize > 0)
            {
                memcpy(&rxData[helper], rxTransferBufferNext, readSize);
            }
			
			g_st_packet.ul_addr = (uint32_t)rxTransferBufferCurrent;
			g_st_packet.ul_size = USART_RX_BUFFER_SIZE;
			pdc_rx_init(g_p_pdc, NULL, &g_st_packet);
			
			temporaryPtr = rxTransferBufferCurrent;
			rxTransferBufferCurrent = rxTransferBufferNext;
			rxTransferBufferNext = temporaryPtr; 

			rxBytesCount = helper + readSize;  
			
			rxCntrPrevious = rxCntr;
		}
		else
		{
			// Read the receive pointer for the buffer currently used by DMA
			// to store the received data
		    rxCntr = pdc_read_rx_counter(g_p_pdc);
		      
		    if( rxCntr >= rxCntrPrevious )
		    {
    		    rxBytesCount = 0;
		    }
		    else
			{
				// Still receiving to the first buffer
				// Copy the data from the first buffer
				readIndex = USART_RX_BUFFER_SIZE - rxCntrPrevious;
				readSize = rxCntrPrevious - rxCntr;
				memcpy(rxData, &rxTransferBufferCurrent[readIndex], readSize);
				
				rxBytesCount = readSize; 
				
				rxCntrPrevious = rxCntr;
			}
		}
	}
	
	return rxBytesCount;
}

