/**
  **************************************************************************************************
  * @file		usart.c
  * @author		Kerem Yollu & Edwin Koch
  * @date		03.11.2021
  * @version	0.4 Unstable
  ************************************************************************************************** 
  * @brief		Implementation of usart.h for the STM32F042K6 MCU
  * 
  * **Detailed Description :**
  * 
  * This source code uses bit manipulation in order to minimise the footprint of pin initialisation 
  * and manipulation. It's based on the CMSIS/Device/ST/STM32F0xx/Include/stm32f042x6.h Header file
  * to get the obtain the right Registers. 
  *
  * @todo: 
  *	- 04.11.21 check if the buad rate calcutation is working good for all awailable baud rates
  *	- 04.11.21 Implment interrupts asap
  *	- 04.11.21 Implment other functionalities as they are needed	
  **************************************************************************************************
*/

#include "usart.h"
#include "pin.h"


#define SYST_CLK 8000000
#define AF1 0x01

#define USART_CHANNEL ((USART_TypeDef *)channel)


void usartInit(	usartNo_t channel, 
				pinNo_t pinTx, 
				pinNo_t pinRx, 
				uint32_t baud,
				usartWordLength_t lenght, 
				uint8_t parity, 
				usartHwFlowCtrl_t flowCtrl)
{
	/* Configuring the pins for the uart*/ 
	usartInitTx(pinTx);
	usartInitRx(pinRx);
		
	//Enable the UART Module on the periferal bus this must be done before setting any register.
	if(channel == usart2)
	{
		RCC->APB1ENR |= RCC_APB1ENR_USART2EN; 
	}
	else
	{
		RCC->APB2ENR |= RCC_APB2ENR_USART1EN; 
	}

		
	USART_CHANNEL->CR1 = 0; // The = 0 is on purpose to set uart to default mode.    
	USART_CHANNEL->CR2 = 0; // The = 0 is on purpose to set uart to default mode.    
	USART_CHANNEL->CR3 = 0; // The = 0 is on purpose to set uart to default mode.   

	
	usartSetBaudRate(channel,baud);
	usartSetWordLenght(channel, lenght);
	usartSetHwFlowCtrl(channel, flowCtrl);

	USART_CHANNEL->CR1 |= USART_CR1_TE; //Enbale trasnmit  
	USART_CHANNEL->CR1 |= USART_CR1_RE; //Enable recieve
	
	//UART Enable shall allwas be at the end of configuration. 
	USART_CHANNEL->CR1 |= USART_CR1_UE; 	
}

void usartInitTx(pinNo_t pinTx)
{
	//pinConfig(pinTx, alternate, def_stage, def_res, def_speed);
	pinSetMode(pinTx,alternate); 
	pinSetAlternate(pinTx, AF1);
}	

void usartInitRx(pinNo_t pinRx)
{
	//pinConfig(pinRx, alternate, def_stage, def_res, def_speed);
	pinSetMode(pinRx,alternate); 
	pinSetAlternate(pinRx, AF1);
}	

//this one is special as the register bist's don't follow eachother. 
void usartSetWordLenght(usartNo_t channel, usartWordLength_t lenght)
{
	if(lenght == seven)
	{
		USART_CHANNEL->CR1 |= USART_CR1_M1; 	
		USART_CHANNEL->CR1 &= ~USART_CR1_M0; 
	}
	else if (lenght == eight)
	{
		USART_CHANNEL->CR1 &= ~USART_CR1_M0; 
		USART_CHANNEL->CR1 &= ~USART_CR1_M1; 	
	}
	else if(lenght == nine)
	{
		USART_CHANNEL->CR1 &= ~USART_CR1_M1; 	
		USART_CHANNEL->CR1 |= USART_CR1_M0; 	
	}
}

static uint16_t usartComputeBaudRate(uint32_t clk, uint32_t baud)
{
	//return((clk + (baud/2U))/baud);
	return(clk/baud);
}	

void usartSetBaudRate(usartNo_t channel, uint32_t baud)	
{
	USART_CHANNEL->BRR = usartComputeBaudRate(SYST_CLK,baud); 
}

void usartSetHwFlowCtrl(usartNo_t channel, usartHwFlowCtrl_t flowCtrl)
{
	if(flowCtrl == noFlowControl)
	{
		USART_CHANNEL->CR3 &= ~USART_CR3_CTSE; 
		USART_CHANNEL->CR3 &= ~USART_CR3_RTSE; 
	}
	else if(flowCtrl == cts)
	{
		USART_CHANNEL->CR3 |= USART_CR3_CTSE; 
	}
	else if(flowCtrl == rts)
	{
		USART_CHANNEL->CR3 |= USART_CR3_RTSE; 
	}
	else if(flowCtrl == ctsRts)
	{
		USART_CHANNEL->CR3 |= USART_CR3_CTSE; 
		USART_CHANNEL->CR3 |= USART_CR3_RTSE; 
	}
}	

void print_Usart(usartNo_t channel,char *ptr) 
{
	uint16_t len = 0; 
	
	while(ptr[len] != '\0')
	{
		usartSendChar(channel, ptr[len]);
		len++;
	}
}

void usartSendChar(usartNo_t channel, uint8_t ch)
{
	// Make sure that TX buffer is empty 
	while(!(USART_CHANNEL->ISR & USART_ISR_TXE)){}
	USART_CHANNEL->TDR = ch; 
}

uint8_t usartGetChar(usartNo_t channel)
{
	// Make sure that TX buffer is empty 
	while(!(USART_CHANNEL->ISR & USART_ISR_RXNE)){}
	return USART_CHANNEL->RDR; 
}
