/*
 * usart2.c
 *
 *  Created on: Feb 26, 2023
 *      Author: francesco
 */


#include "usart2.h"
#include "stm32f446xx.h"



#define USART2_RX_BUFFLENGTH	512
volatile static u8 usart2_rx_buffer [USART2_RX_BUFFLENGTH];
volatile static u16 u2rx_sptr, u2rx_eptr;	// define start and end pointers to the circular buffer


#define USART2_TX_BUFFLENGTH	1024
volatile static u8 usart2_tx_buffer [USART2_TX_BUFFLENGTH];
volatile static u16 u2tx_tail, u2tx_head;	// define start and end pointers to the circular buffer




// in case of either of the buffer overflow, the size must be increased or code
// modified for assuring proper operation

static void usart2_rx_buffer_overflow (void) {

	while (1);
}

static void usart2_tx_buffer_overflow (void) {

	while (1);
}




// enable interrupt for tx buffer empty and rx buffer full
void usart2_init (void) {

	// enable USART and GPIOA clock
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
	RCC->APB1ENR |= RCC_APB1ENR_USART2EN;

	   /* =================================== 
   	  USART2 GPIO Configuration
       - PA2     ------> USART2_TX, AF7
       - PA3     ------> USART2_RX, AF7
	  ==================================== */	
	// PA2,3 alternate function mode
	GPIOA->MODER |= (0b10 << (2*2)) | (0b10 << (3*2));
	// PA2,3 high speed
	GPIOA->OSPEEDR |= (0b11 << (2*2)) | (0b11 << (3*2));
	// PA2,3 AF7
	GPIOA->AFR[0] |= (0b0111 << (2*4)) | (0b0111 << (3*4));


	// init all variables used by the usart handling code
	u2rx_sptr = 0;
	u2rx_eptr = 0;
	u2tx_tail = 0;
	u2tx_head = 0;

	for (u16 i=0; i<USART2_RX_BUFFLENGTH; ++i)
		usart2_rx_buffer [i] = 0;

	for (u16 i=0; i<USART2_TX_BUFFLENGTH; ++i)
		usart2_tx_buffer [i] = 0;


	// to derive 115200 baud rate from 16MHz clock the divisor must be 
	// set to 8, 11
	USART2->BRR = (8 << USART_BRR_DIV_Mantissa_Pos) | (11 << USART_BRR_DIV_Fraction_Pos);
	// enable even (default) parity, oversampling 16 (default)
	//USART2->CR1 |= (USART_CR1_PCE | USART_CR1_M);


	// enable receiver, rx interrupt and usart peripheral
	USART2->CR1 |= (USART_CR1_UE | USART_CR1_RXNEIE | USART_CR1_RE);

	// enable NVIC interrupt
	NVIC_SetPriority (USART2_IRQn, 0);
	NVIC_EnableIRQ (USART2_IRQn);
}




void usart2_enable (void) {

	// enable receiver and peripheral
	USART2->CR1 |= (USART_CR1_UE_Msk | USART_CR1_RXNEIE | USART_CR1_RE);
}

void usart2_disable (void) {

	// disable receiver, transmitter and uart peripheral
	USART2->CR1 &= ~(USART_CR1_UE_Msk | USART_CR1_TE | USART_CR1_RE);
}



// implement interrupt vector. There is only one interrupt vector for the
// entire usart peripheral, so interrupt cause must be determined by
// reading ir flags


void USART2_IRQHandler (void) {

	// test for tx register empty flag by also testing if the transmitter is enabled
	// to make sure that a transmission is going on and it wasn't a spurius interrupt
	// fiered
	if ((USART2->CR1 & USART_CR1_TXEIE_Msk) != 0 && (USART2->SR & USART_SR_TXE_Msk) != 0) {

		if (u2tx_tail != u2tx_head) {
			u2tx_tail = (u2tx_tail + 1) & (USART2_TX_BUFFLENGTH - 1);
			// if there is data to be transmitted transmit it
			if (u2tx_tail != u2tx_head) {

				USART2->DR = usart2_tx_buffer [u2tx_tail];
			}
			// otherwise disable the transmitter module
			else {
				USART2->CR1 &= ~ (USART_CR1_TXEIE);
			}
		}

		if (u2tx_tail > u2tx_head) {
			//__asm__("BKPT");
		}
	}


	// test for rx data ready flag
	if ((USART2->SR & USART_SR_RXNE_Msk) != 0) {
		// temporarily increment the end pointer of the rx circular buffer and clip it
		// to the buffer length
		u16 u2rx_eptr_temp = (u2rx_eptr + 1) & (USART2_RX_BUFFLENGTH -1);
		u8 data = USART2->DR;

		// do not overwrite the buffer
		if (u2rx_eptr_temp != u2rx_sptr) {
			usart2_rx_buffer [u2rx_eptr++] = data;
			u2rx_eptr = u2rx_eptr_temp;
		}
		else {
			usart2_rx_buffer_overflow ();
		}
	}
}


u32 usart2_flush_rx () {

	u32 len = u2rx_eptr < u2rx_sptr ? (USART2_RX_BUFFLENGTH - u2rx_eptr + u2rx_eptr) :
				                      (u2rx_eptr - u2rx_sptr);

	u2rx_eptr = 0;
	u2rx_sptr = 0;
	return len;
}

u32 usart2_flush_rx_len (u32 count) {

	u32 avail = usart2_data_available ();
	u32 toFlush = count <= avail? count: avail;

	u2rx_sptr = (u2rx_sptr + toFlush) & (USART2_RX_BUFFLENGTH-1);
}




void usart2_putch (u8 ch) {

	// temporarily increment the end pointer of the tx circular buffer and clip it
	// to the buffer length
	u16 u2tx_head_temp = (u2tx_head + 1) & (USART2_TX_BUFFLENGTH-1);

	// signal if buffer overflow would have occurred
	if (u2tx_head_temp == u2tx_tail)
		usart2_tx_buffer_overflow ();

	// wait until there is space available in the tx buffer
	while (u2tx_head_temp == u2tx_tail);


	// do not allow the peripheral to fire a tx interrupt at this point
	clr_bit (USART2->CR1, USART_CR1_TXEIE);

	// if no transmission was already going on
	if (u2tx_head == u2tx_tail) {

		usart2_tx_buffer [u2tx_head] = ch;
		u2tx_head = u2tx_head_temp;
		USART2->DR = ch;
		USART2->CR1 |= (USART_CR1_TXEIE | USART_CR1_TE);
	}
	else {
		usart2_tx_buffer [u2tx_head] = ch;
		u2tx_head = u2tx_head_temp;
		USART2->CR1 |= USART_CR1_TXEIE;
	}
}

void usart2_write_buff (u8 * buff, u32 len) {

	for (u32 i=0; i < len; ++i)
		usart2_putch (buff [i]);
}


void usart2_puts (char * buff) {

	while (*buff != 0)
		usart2_putch (*buff++);
}


u32 usart2_read_buff (u8* buff, u32 len) {

	u32 readLen = 0;

	while (u2rx_sptr != u2rx_eptr) {

		buff [readLen++] = usart2_rx_buffer [u2rx_sptr];
		u2rx_sptr = (u2rx_sptr +1) & (USART2_RX_BUFFLENGTH - 1);
	}
	return readLen;
}

/*
 function usart2_getch ();

 @param c: pointer to the variable to write the read character into
 @return
 	 1: there were data to read
 	 0: no valid data was available to be read
 */

u8 usart2_getch (u8 * c) {

	if (u2rx_sptr != u2rx_eptr) {

		u8 data = usart2_rx_buffer [u2rx_sptr];
		u2rx_sptr = (u2rx_sptr +1) & (USART2_RX_BUFFLENGTH - 1);
		*c = data;
		return 1;
	}
	return 0;
}

u32 usart2_data_available (void) {

	u32 head = u2rx_eptr;
	u32 tail = u2rx_sptr;

	if (tail != head) {

		if (head < tail) {
			return (USART2_RX_BUFFLENGTH - tail + head);
		}else {
			return (head - tail);
		}
	}
	return 0;
}



