//------------------------------------------------------------------------------
// File:          us0.c
// Function:      USART US0 driver
// Supported chip(s):
//    - AT91SAM7XC128
//    - AT91SAM7XC256
// Supported toolchain(s):
//    - IAR Embedded Workbench
// Date created:  28 April 2006
// Created by:    JJo
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// Includes
//------------------------------------------------------------------------------

#include "us0.h"

#ifdef USE_US0

//------------------------------------------------------------------------------
// Global variables
//------------------------------------------------------------------------------

// Cyclic buffer and indexes
volatile char cyclicBuffer[US0_BUFFER_SIZE];
volatile unsigned int cyclicDataStart;  // Start of buffered data
volatile unsigned int cyclicDataEnd;    // End of buffered data
volatile unsigned int cyclicDataSize;   // Size of buffered data

//------------------------------------------------------------------------------
// Interrupts
//------------------------------------------------------------------------------

/**
 * Name:      us0_interruptHandler
 * Purpose:   Stores a character in a cyclic buffer when it is received on US0
 */
void us0_interruptHandler() {

  // Local variables
  char input;

  input = (char) AT91F_US_GetChar(AT91C_BASE_US0);

  // Check if buffer is not full
  if (cyclicDataSize < US0_BUFFER_SIZE) {

    cyclicBuffer[cyclicDataEnd] = input;
    cyclicDataEnd = (cyclicDataEnd+1) & US0_BUFFER_SIZE_MASK;
    cyclicDataSize++;
  }
}

//------------------------------------------------------------------------------
// Inline functions
//------------------------------------------------------------------------------

/**
 * Name:      AT91F_US_TxEmpty
 * Purpose:   Returns whether the transmit buffer is empty or not
 * Input:
 *  - USART peripheral address
 * Output:    1 if there is not character to process, 0 otherwise
 */
__inline unsigned int AT91F_US_TxEmpty(AT91PS_USART pUSART) {

  return ((pUSART->US_CSR & AT91C_US_TXEMPTY) >> 9);
}

//------------------------------------------------------------------------------
// Functions
//------------------------------------------------------------------------------

/**
 * Name:      us0_init
 * Purpose:   Initializes the USART0
 */
void us0_init() {

  debug_printf("USART0: Initializing ...\n");

  // Configure PMC and PIO
  AT91F_US0_CfgPMC();
  AT91F_US0_CfgPIO();

  // Configure parameters
  AT91F_US_Configure(AT91C_BASE_US0, MCK, US0_MODE, US0_BAUDRATE, 0);
  AT91F_US_EnableRx(AT91C_BASE_US0);
  AT91F_US_EnableTx(AT91C_BASE_US0);

  // Initialize cyclic buffer
  cyclicDataStart = 0;
  cyclicDataEnd = 0;
  cyclicDataSize = 0;

  // Configure rxReady interrupt
  AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_US0, US0_PRIORITY,
                        AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL,
                        us0_interruptHandler);
  AT91F_US_EnableIt(AT91C_BASE_US0, AT91C_US_RXRDY);
  AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_US0);

  debug_printf("USART0: Initialization done.\n");
}

/**
 * Name:      us0_cleanup
 * Purpose:   Cleans-up the USART0, returning it to its reset configuration
 */
void us0_cleanup() {

  debug_printf("USART0: Cleaning up ...\n");
  AT91F_US_Close(AT91C_BASE_US0);
  AT91F_PMC_DisablePeriphClock(AT91C_BASE_PMC, 1 << AT91C_ID_US0);
  AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, AT91C_PA0_RXD0 | AT91C_PA1_TXD0 | AT91C_PA3_RTS0 |
		            AT91C_PA2_SCK0 | AT91C_PA4_CTS0);
  debug_printf("USART0: Cleanup done.\n");
}

/**
 * Name:      us0_read
 * Purpose:   Reads characters from the cyclic buffer
 * Inputs:
 *  - Buffer in which to store read characters. If NULL, the whole
 *    cyclic buffer is emptied
 *  - Size of buffer
 * Output: Number of characters read.
 */
unsigned int us0_read(void * vBuffer, unsigned int size) {

  // Local variables
  unsigned int bytes;
  char * pBuffer = (char *) vBuffer;

  // In case buffer is NULL, empty buffer
  if (vBuffer == NULL) {

    bytes = cyclicDataSize;
    cyclicDataStart += bytes;
    cyclicDataSize -= bytes;
    return bytes;
  }

  // Loop through available characters, storing them in the buffer
  bytes = 0;

  while ((bytes < size) && (cyclicDataSize > 0)) {
  
    pBuffer[bytes] = cyclicBuffer[cyclicDataStart];
    cyclicDataStart = (cyclicDataStart+1)%US0_BUFFER_SIZE;
    cyclicDataSize--;

    bytes++;
  }

  return bytes;
}

/**
 * Name:      us0_poll
 * Purpose:   Returns the number of bytes available in the read buffer
 * Output:    Number of bytes that can be read in one call
 */
unsigned int us0_poll() {

  return cyclicDataSize;
}

/**
 * Name:      us0_writeChar
 * Purpose:   Sends a character on the USART0 line
 * Input:
 *  - Character to send
 */
void us0_writeChar(char input) {

  // Wait for transmitter to be ready, send char, wait for completion
  while (!AT91F_US_TxReady(AT91C_BASE_US0));
  AT91F_US_PutChar(AT91C_BASE_US0, input);
  while (!AT91F_US_TxEmpty(AT91C_BASE_US0));
}

#endif // USE_US0
