
/*
 * 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 "serial.h"

/* workspaces for the USARTS being used */
usart_ctl uc2;
usart_ctl uc3;



/* 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.
 */
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) {
		// 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 ((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 Initialise control structure
 */
void init_usart_ctl(usart_ctl * instance, USART_TypeDef * hardware)
{
	instance->Instance = hardware;

	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
void USART3_IRQHandler(void)
{
	UART_IRQHandler(&uc3);
}

void USART2_IRQHandler(void)
{
	UART_IRQHandler(&uc2);
}

