
/*
 * 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
#if defined SERIAL_UART4
usart_ctl uc4;
#endif
#if defined SERIAL_UART5
usart_ctl uc5;
#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->Handle, 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->Handle, UART_IT_RXNE);

  return result;
}

uint16_t SerialTransmitSpace(usart_ctl *instance)
{
  return TX_USART_BUFF_SIZ - instance->tx_usart_count;
}

inline uint8_t
PollSerial(usart_ctl *instance)
{
  uint8_t rc;

  __HAL_UART_DISABLE_IT(instance->Handle, UART_IT_RXNE);
  rc = (instance->rx_usart_buffer_full || (instance->rx_usart_in_Ptr != instance->rx_usart_out_Ptr));
  __HAL_UART_ENABLE_IT(instance->Handle, 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->Handle, UART_IT_RXNE);
  while (!instance->rx_usart_buffer_full && (instance->rx_usart_in_Ptr == instance->rx_usart_out_Ptr))
  {
    __HAL_UART_ENABLE_IT(instance->Handle, UART_IT_RXNE);
    __WFI(); /* wait for something */
    __HAL_UART_DISABLE_IT(instance->Handle, 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->Handle, 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->Handle, 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->Handle, 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->Handle, UART_IT_TXE);
}

void UART_IRQHandler(usart_ctl *instance)
{

  __disable_irq();
  uint32_t rxStatus; // status from USART receiver

  rxStatus = instance->Handle->Instance->SR; // read the status bits - this resets all the hardware signalling flags

  if ((rxStatus & USART_SR_LBD))
    __HAL_UART_CLEAR_FLAG(instance->Handle, USART_SR_LBD);

  if ((rxStatus & USART_SR_RXNE) != RESET)
  {
    // no error has occurred...
    uint8_t rxChar = (uint8_t)(instance->Handle->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 ((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->Handle->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;
      }
    }
    if (instance->tx_usart_count == 0)
    {
      __HAL_UART_DISABLE_IT(instance->Handle, 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->Handle->Instance->SR & USART_SR_TC) != RESET)
  {
  };
}

/****
 * @brief Initialise control structure
 */
void init_usart_ctl(usart_ctl *instance, UART_HandleTypeDef *handle)
{

  instance->Handle = handle;

  /* 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->Handle, UART_IT_TXE);
  __HAL_UART_DISABLE_IT(instance->Handle, 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;
}

void setBaud(usart_ctl *ctl, uint32_t baud)
{
  ctl->Handle->Init.BaudRate = baud;
  __disable_irq();
  HAL_UART_Init(ctl->Handle);
  __enable_irq();
}



void sendString(usart_ctl *ctl, char const * string , int length)
{
  for (int i = 0; i < length; i++)
    PutCharSerial(ctl, string[i]);
}

/////////////////////////////////////////////////////////
/// 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
#if defined SERIAL_UART4
void UART4_IRQHandler(void)
{
  UART_IRQHandler(&uc4);
}
#endif
#if defined SERIAL_UART5
void UART5_IRQHandler(void)
{
  UART_IRQHandler(&uc5);
}
#endif