
/*
 * serial.c
 *
 *  Created on: 4 Jan 2016
 *      Author: Mike
 *
 *      This (ab)uses the STMCubeMX HAL declarations to implement a generic STM32F1xx
 *      USART driver layer
 */

#include "libSerial/serial.h"

/* workspaces for the USARTS being used */
#if defined SERIAL_UART1
usart_ctl uc1;
#endif
#if defined SERIAL_UART2
usart_ctl uc2;
#endif
#if defined SERIAL_UART3
usart_ctl uc3;
#endif

/* returns the number of characters received by the Rx USART */
uint16_t
SerialCharsReceived (usart_ctl *instance)
{
  uint16_t result = 0;	// assume no characters received yet
  __HAL_UART_DISABLE_IT (instance, UART_IT_RXNE);

  if (instance->rx_usart_buffer_full)
    {	// buffer is full...
      result = RX_USART_BUFF_SIZ;
    }
  else if (instance->rx_usart_in_Ptr >= instance->rx_usart_out_Ptr)
    {	// buffer has not wrapped...
      result = instance->rx_usart_in_Ptr - instance->rx_usart_out_Ptr;
    }
  else
    {	// buffer has possibly wrapped...
      result = RX_USART_BUFF_SIZ - instance->rx_usart_out_Ptr
	  + instance->rx_usart_in_Ptr;
    }
  __HAL_UART_ENABLE_IT (instance, UART_IT_RXNE);

  return result;
}

inline uint8_t
PollSerial (usart_ctl *instance)
{
  uint8_t rc;

  __HAL_UART_DISABLE_IT (instance, UART_IT_RXNE);
  rc = (instance->rx_usart_buffer_full
      || (instance->rx_usart_in_Ptr != instance->rx_usart_out_Ptr));
  __HAL_UART_ENABLE_IT (instance, UART_IT_RXNE);

  return rc;
}

/***!
 * @brief return the next character in the Serial input buffer
 * This function will wait until a character arrives.
 */
inline uint8_t
GetCharSerial (usart_ctl *instance)
{
  uint8_t c;
  __HAL_UART_DISABLE_IT (instance, UART_IT_RXNE);
  while (!instance->rx_usart_buffer_full
      && (instance->rx_usart_in_Ptr == instance->rx_usart_out_Ptr))
    {
      __HAL_UART_ENABLE_IT (instance, UART_IT_RXNE);
      __WFI (); /* wait for something */
      __HAL_UART_DISABLE_IT (instance, UART_IT_RXNE);
    }

  c = instance->rx_usart_buff[instance->rx_usart_out_Ptr];
  instance->rx_usart_buffer_full = 0; /* removed character */
  instance->rx_usart_out_Ptr++;
  if (instance->rx_usart_out_Ptr >= RX_USART_BUFF_SIZ)
    {
      instance->rx_usart_out_Ptr = 0;
    }
  __HAL_UART_ENABLE_IT (instance, UART_IT_RXNE);
  return c;
}

/*
 * \brief
 * void EnableSerialRxInterrupt(void) - this function is used from the interrupt handler and the main scheduler loop
 * to enable the serial rx interrupt after resetting the serial rx buffer...
 */
inline void
EnableSerialRxInterrupt (usart_ctl *instance)
{
  /* cheat here - this is a macro and I have the same Instance member as the HAL handle, with the same meaning */
  __HAL_UART_ENABLE_IT (instance, UART_IT_RXNE);

}

/****!
 * @brief send a character to the serial USART via placing it in the serial buffer
 *
 */

static void
PutCharSerialFIFO (usart_ctl *instance, uint8_t c)
{
  __HAL_UART_DISABLE_IT (instance, UART_IT_TXE);

  instance->tx_usart_buff[instance->tx_usart_in_Ptr++] = c;
  instance->tx_usart_count += 1;
  if (instance->tx_usart_in_Ptr >= TX_USART_BUFF_SIZ)
    {
      instance->tx_usart_in_Ptr = 0;
    }
  /* Handle overrun by losing oldest characters */
  if (instance->tx_usart_in_Ptr == instance->tx_usart_out_Ptr)
    {
      instance->tx_usart_out_Ptr++;
      if (instance->tx_usart_out_Ptr >= TX_USART_BUFF_SIZ)
	{
	  instance->tx_usart_out_Ptr = 0;
	}
    }

  instance->tx_usart_running = 1;
  __HAL_UART_ENABLE_IT (instance, UART_IT_TXE);
}

void
UART_IRQHandler (usart_ctl *instance)
{

  __disable_irq ();
  uint32_t rxStatus;	// status from USART receiver

  rxStatus = instance->Instance->SR;// read the status bits - this resets all the hardware signalling flags

  if ((rxStatus & USART_SR_RXNE)!= RESET)
    {
      // no error has occurred...
      uint8_t rxChar = (uint8_t) (instance->Instance->DR & 0xff);// read the bottom 8-bits only

      if (!instance->rx_usart_buffer_full)
	{
	  instance->rx_usart_buff[instance->rx_usart_in_Ptr++] = rxChar;

	  if (instance->rx_usart_in_Ptr >= RX_USART_BUFF_SIZ)
	    {
	      instance->rx_usart_in_Ptr = 0;
	    }
	  if (instance->rx_usart_in_Ptr == instance->rx_usart_out_Ptr)
	    {
	      instance->rx_usart_buffer_full = 1; /* buffer overrun */
	    }
	}
    }
  /* check for transmitter interrupt : this code is used */
  if (instance->tx_usart_running && ((rxStatus & USART_SR_TXE) != RESET))
    {

      /* Only enable the transmitter when baud detect has completed or check expired.
       * and the software is ready for it to be enabled as programming mode is wanting
       * to receive a response and that can get blocked if we're streaming a lot of debug messages*/
      if (instance->tx_usart_in_Ptr != instance->tx_usart_out_Ptr)
	{
	  instance->Instance->DR =
	      instance->tx_usart_buff[instance->tx_usart_out_Ptr++];
	  if (instance->tx_usart_count != 0)
	    instance->tx_usart_count -= 1;

	  if (instance->tx_usart_out_Ptr >= TX_USART_BUFF_SIZ)
	    {
	      instance->tx_usart_out_Ptr = 0;
	    }
	}
      else
	{
	  __HAL_UART_DISABLE_IT (instance, UART_IT_TXE);
	  instance->tx_usart_running = 0;
	}
    }

  __enable_irq ();
}

void
PutCharSerial (usart_ctl *instance, uint8_t c)
{
  // Put character in Crayon/Pen interface
  PutCharSerialFIFO (instance, c);
}

/*
 * \brief
 * ResetTxBuffer(void) - resets the serial transmitter buffer
 */
void
ResetTxBuffer (usart_ctl *instance)
{

  instance->tx_usart_out_Ptr = 0;
  instance->tx_usart_running = 0;
  instance->tx_usart_in_Ptr = 0; /* setup in pointer last to drop any chars come in */
  instance->tx_usart_count = 0;

}

/*
 * \brief
 * void ResetRxBuffer(void) - resets the serial receiver buffer
 */
void
ResetRxBuffer (usart_ctl *instance)
{

  instance->rx_usart_out_Ptr = 0;
  instance->rx_usart_buffer_full = 0;
  instance->rx_usart_in_Ptr = 0; /* setup in pointer last to drop any chars come in */

}

/***!
 * @brief Flush Serial input and output buffers
 */
void
FlushSerial (usart_ctl *instance)
{
  ResetRxBuffer (instance);
  ResetTxBuffer (instance);
}

/***!
 * @brief check if tx buffer is empty...
 */
uint8_t
TxBufferEmpty (usart_ctl *instance)
{
  return (0 == instance->tx_usart_count );
}

/***!
 * @brief wait for transmission to finish
 */

void TxWaitEmpty(usart_ctl *instance)
{
  while (instance->tx_usart_count ||
       (instance->Instance->SR & USART_SR_TC) != RESET) {};
}

/****
 * @brief Initialise control structure
 */
void
init_usart_ctl (usart_ctl *instance, USART_TypeDef * usart )
{

  instance->Instance = usart;

  /* cheat here - this is a macro and I have the same Instance member as the HAL handle, with the same meaning */
   __HAL_UART_DISABLE_IT (instance, UART_IT_TXE);
  __HAL_UART_DISABLE_IT (instance, UART_IT_RXNE);

  instance->tx_usart_in_Ptr = 0;
  instance->tx_usart_out_Ptr = 0;
  instance->tx_usart_running = 0;
  instance->tx_usart_count = 0;

  instance->rx_usart_in_Ptr = 0;
  instance->rx_usart_out_Ptr = 0;
  instance->rx_usart_buffer_full = 0;

}

/////////////////////////////////////////////////////////
/// Moved from generated code  to avoid crappy HAL handler
#if defined SERIAL_UART1
void USART1_IRQHandler(void)
{
	UART_IRQHandler(&uc1);
}
#endif
#if defined SERIAL_UART2
void USART2_IRQHandler(void)
{
	UART_IRQHandler(&uc2);
}
#endif
#if defined SERIAL_UART3
void USART3_IRQHandler(void)
{
	UART_IRQHandler(&uc3);
}
#endif
