//********************************************************************
//* STM32RingBuffer.c
//* Adapted by: Fred Eady
//* Last Update: 08-28-2024
//* Notes:
//*		Add the following code to the stm32xxxx_it.c file
//* 	Add this line to Private Variables
//*			extern uint16_t timeout;
//*		Add this line to the Private Function Prototypes
//*			extern void ringbuffer_IRQHandler(UART_HandleTypeDef *huart);
//*		Add this line to the External Variables - x is huart number
//*			extern UART_HandleTypeDef huartx;
//********************************************************************

#include <string.h>
#include <stdint.h>
#include <STM32Ringbuffer.h>

// Function Prototypes
void writeRxRing(uint8_t bite, ringBuffer *buffer);

// Assign an alias for the huartx
extern UART_HandleTypeDef huart1;
#define uartAlias &huart1

// timeout in milliseconds - based on SysTick
#define timeoutVal 500
uint16_t timeout;

// Initialize/Zero RingBuffer structs
ringBuffer rx_buffer = { { 0 }, 0, 0};
ringBuffer tx_buffer = { { 0 }, 0, 0};

// Create RingBuffer struct pointers
ringBuffer *p_rx_buffer;
ringBuffer *p_tx_buffer;

//********************************************************************
//* Initialize the RX and TX RingBuffers
//********************************************************************
void RingBuffer_Init(void)
{
  p_rx_buffer = &rx_buffer;
  p_tx_buffer = &tx_buffer;

  // Enable the UART Error Interrupt: (Frame error, noise error, overrun error)
  __HAL_UART_ENABLE_IT(uartAlias, UART_IT_ERR);

  // Enable the UART Data Register not empty Interrupt
  __HAL_UART_ENABLE_IT(uartAlias, UART_IT_RXNE);
}

//********************************************************************
//* Store a character in the RX RingBuffer
//********************************************************************
void writeRxRing(uint8_t bite, ringBuffer *buffer)
{
  uint16_t i = (buffer->head + 1) % ringBufSize;

  // Prevent buffer overflow
  if(i != buffer->tail) {
    buffer->buffer[buffer->head] = bite;
    buffer->head = i;
  }
}

//********************************************************************
//* Read a character from the RX RingBuffer
//********************************************************************
uint8_t readRxRing(void)
{
  // if the head isn't ahead of the tail, we don't have any characters
  if(p_rx_buffer->head == p_rx_buffer->tail)
  {
    return -1;
  }
  else
  {
    uint8_t biteRead = p_rx_buffer->buffer[p_rx_buffer->tail];
    p_rx_buffer->tail = (p_rx_buffer->tail + 1) % ringBufSize;
    return biteRead;
  }
}

//********************************************************************
//* Store a character in the TX RingBuffer
//********************************************************************
void writeTxRing(uint8_t txChar)
{
	if (txChar >= 0)
	{
		int i = (p_tx_buffer->head + 1) % ringBufSize;
		while (i == p_tx_buffer->tail);

		p_tx_buffer->buffer[p_tx_buffer->head] = txChar;
		p_tx_buffer->head = i;

		// Enable UART transmission interrupt
		__HAL_UART_ENABLE_IT(uartAlias, UART_IT_TXE);
	}
}

//********************************************************************
//* Check for characters in RX RingBuffer
//********************************************************************
uint16_t IsCharInQueue(void)
{
  return (ringBufSize + p_rx_buffer->head - p_rx_buffer->tail) % ringBufSize;
}

//********************************************************************
//* Reset the RX RingBuffer
//********************************************************************
void resetRxRing (void)
{
	memset(p_rx_buffer->buffer,'\0', ringBufSize);
	p_rx_buffer->head = 0;
	p_rx_buffer->tail = 0;
}

//********************************************************************
//* RingBuffer Interrupt Handler
//********************************************************************
void ringbuffer_IRQHandler (UART_HandleTypeDef *huart)
{
	  uint32_t isrflags   = READ_REG(huart->Instance->ISR);
	  uint32_t cr1its     = READ_REG(huart->Instance->CR1);

    /* if DR is not empty and the Rx Int is enabled */
    if (((isrflags & USART_ISR_RXNE_RXFNE) != RESET) && ((cr1its & USART_CR1_RXNEIE_RXFNEIE) != RESET))
    {
    	 /******************
    	    	      *  @note   PE (Parity error), FE (Framing error), NE (Noise error), ORE (Overrun
    	    	      *          error) and IDLE (Idle line detected) flags are cleared by software
    	    	      *          sequence: a read operation to USART_SR register followed by a read
    	    	      *          operation to USART_DR register.
    	    	      * @note   RXNE flag can be also cleared by a read to the USART_DR register.
    	    	      * @note   TC flag can be also cleared by software sequence: a read operation to
    	    	      *          USART_SR register followed by a write operation to USART_DR register.
    	    	      * @note   TXE flag is cleared only by a write to the USART_DR register.

    	 *********************/
    	huart->Instance->ISR;                       /* Read status register */
        uint8_t biteIn = huart->Instance->RDR;     /* Read data register */
        writeRxRing (biteIn, p_rx_buffer);  // store data in buffer
        return;
    }

    /*If interrupt is caused due to Transmit Data Register Empty */
    if (((isrflags & USART_ISR_TXE_TXFNF) != RESET) && ((cr1its & USART_CR1_TXEIE_TXFNFIE) != RESET))
    {
    	if(tx_buffer.head == tx_buffer.tail)
    	    {
    	      // Buffer empty, so disable interrupts
    	      __HAL_UART_DISABLE_IT(huart, UART_IT_TXE);

    	    }

    	 else
    	    {
    	      // There is more data in the output buffer. Send the next byte
    	      uint8_t biteOut = tx_buffer.buffer[tx_buffer.tail];
    	      tx_buffer.tail = (tx_buffer.tail + 1) % ringBufSize;

    	      /******************
    	      *  @note   PE (Parity error), FE (Framing error), NE (Noise error), ORE (Overrun
    	      *          error) and IDLE (Idle line detected) flags are cleared by software
    	      *          sequence: a read operation to USART_SR register followed by a read
    	      *          operation to USART_DR register.
    	      * @note   RXNE flag can be also cleared by a read to the USART_DR register.
    	      * @note   TC flag can be also cleared by software sequence: a read operation to
    	      *          USART_SR register followed by a write operation to USART_DR register.
    	      * @note   TXE flag is cleared only by a write to the USART_DR register.

    	      *********************/

    	      huart->Instance->ISR;
    	      huart->Instance->TDR = biteOut;

    	    }
    	return;
    }
}

