/*!
 * @file    	apm32f10x_usart.c
 * @brief	 	This file provides all the USART firmware functions
 *
 * @details	
 *
 * @version		V1.0.0
 * @author  	
 * @date    	2019-8-7
 *
 */

#include "apm32f10x_usart.h"
#include "apm32f10x_rcm.h"

/*!
 * @brief		Reset usart peripheral registers to their default reset values
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_Reset(USART_T* usart)
{
	if(USART1 == usart)
	{
		RCM_EnableAPB2PeriphReset(RCM_APB2_PERIPH_USART1);
		RCM_DisableAPB2PeriphReset(RCM_APB2_PERIPH_USART1);
	}
	else if(USART2 == usart)
	{
		RCM_EnableAPB1PeriphReset(RCM_APB1_PERIPH_USART2);
		RCM_DisableAPB1PeriphReset(RCM_APB1_PERIPH_USART2);
	}
	else if(USART3 == usart)
	{
		RCM_EnableAPB1PeriphReset(RCM_APB1_PERIPH_USART3);
		RCM_DisableAPB1PeriphReset(RCM_APB1_PERIPH_USART3);
	}	
	else if(UART4 == usart)
	{
		RCM_EnableAPB1PeriphReset(RCM_APB1_PERIPH_UART4);
		RCM_DisableAPB1PeriphReset(RCM_APB1_PERIPH_UART4);
	}
	else if(UART5 == usart)
	{
		RCM_EnableAPB1PeriphReset(RCM_APB1_PERIPH_UART5);
		RCM_DisableAPB1PeriphReset(RCM_APB1_PERIPH_UART5);
	}	
}

/*!
 * @brief		Config the USART peripheral according to the specified parameters in the USART_InitStruct
 *
 * @param		uart			Select the USART or the UART peripheral
 *
 * @param		configStruct	pointer to a USART_ConfigStruct_T structure
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_Config(USART_T* uart, USART_ConfigStruct_T *configStruct)
{
	uint32_t temp, fCLK, intDiv, fractionalDiv;
	
	/** WLS, PCEN, TXEN, RXEN */
	temp = uart->CTRL1;
	temp &= 0xE9F3;
	temp |= configStruct->mode | configStruct->parity | configStruct->wordLength;
	uart->CTRL1 = temp;

	/** STOP bits */
	temp = uart->CTRL2;
	temp &= 0xefff;
	temp |= configStruct->stopBits;
	uart->CTRL2 = temp;

	/** Hardware Flow Control */
	temp = uart->CTRL3;
	temp &= 0xFCFF;
	temp |= (uint32_t)configStruct->hardwareFlowCtrl;
	uart->CTRL3 = temp;

	if(uart == USART1)
	{
		RCM_GetPCLKFreq(NULL, &fCLK);
	}
	else
	{
		RCM_GetPCLKFreq(&fCLK, NULL);
	}
	
	intDiv = ((25 * fCLK) / (4 * (configStruct->baudRate))); 
	temp = (intDiv / 100) << 4;
	fractionalDiv = intDiv - (100 * (temp >> 4));
	temp |= ((((fractionalDiv * 16) + 50) / 100)) & ((uint8_t)0x0F);
	
	uart->BR = temp;
}

/*!
 * @brief		Fills each USART_InitStruct member with its default value
 *
 * @param		configStruct	pointer to a USART_ConfigStruct_T structure which will be initialized
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_StructInit(USART_ConfigStruct_T* configStruct)
{
	configStruct->baudRate = 9600;
	configStruct->wordLength = USART_WORD_LEN_8B;
	configStruct->stopBits = USART_STOP_BIT_1;
	configStruct->parity = USART_PARITY_NONE ;
	configStruct->mode = USART_MODE_TX_RX;
	configStruct->hardwareFlowCtrl = USART_FLOW_CTRL_NONE;  
}

/*!
 * @brief		Enable/Disable CK PIN
 *
 * @param		uart	Select the USART or the UART peripheral
 *
 * @param		ckPin	Enable/Disable CK PIN
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_ConfigCKPin(USART_T* uart, USART_CK_PIN_T ckPin)
{
	uart->CTRL2_B.CKPEN = ckPin;
}

/*!
 * @brief		Config clock polarity
 *
 * @param		uart		Select the USART or the UART peripheral
 *
 * @param		polarity	specifies the clock polarity
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_CofigClkPolarity(USART_T* uart, USART_CLKPOL_T polarity)
{
	uart->CTRL2_B.CLKPOL = polarity;
}

/*!
 * @brief		Config clock phase
 *
 * @param		uart		Select the USART or the UART peripheral
 *
 * @param		phase		specifies the clock phase
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_ConfigClkPhase(USART_T* uart, USART_CLKPHA_T phase)
{
	uart->CTRL2_B.CLKPHA = phase;
}

/*!
 * @brief		Config last bit clock
 *
 * @param		uart		Select the USART or the UART peripheral
 *
 * @param		lbc			Enable/Disable last bit clock
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_ConfigLastBitClock(USART_T* uart, USART_LBC_T lbc)
{
	uart->CTRL2_B.LBCEN = lbc;
}


/*!
 * @brief		Enables the specified USART peripheral
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_Enable(USART_T* usart)
{
	usart->CTRL1_B.UEN = BIT_SET;
}

/*!
 * @brief		Disable the specified USART peripheral
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_Disable(USART_T* usart)
{
	usart->CTRL1_B.UEN = BIT_RESET;
}

/*!
 * @brief		Transmitter Enable
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_EnableTX(USART_T * usart)
{
	usart->CTRL1_B.TXEN = BIT_SET;
}

/*!
 * @brief		Transmitter Disable
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_DisableTX(USART_T * usart)
{
	usart->CTRL1_B.TXEN = BIT_RESET;
}

/*!
 * @brief		Receiver enable
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_EnableRX(USART_T * usart)
{
	usart->CTRL1_B.RXEN = BIT_SET;
}

/*!
 * @brief		Receiver disable
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_DisableRX(USART_T * usart)
{
	usart->CTRL1_B.RXEN = BIT_RESET;
}

/*!
 * @brief		Disables the specified USART interrupts
 *
 * @param		usart		Select the USART or the UART peripheral
 *
 * @param		interrupt	specifies the USART interrupt sources to be enabled or disabled
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_DisableInterrupt(USART_T *usart, USART_INT_T interrupt)
{
	uint32_t temp;

	temp = (uint32_t)~(interrupt & 0xffff);

	if(interrupt & 0X10000)
	{
		usart->CTRL1 &= temp;
	}

	if(interrupt & 0X20000)
	{
		usart->CTRL2 &= temp;
	}

	if(interrupt & 0X40000)
	{
		usart->CTRL3 &= temp;
	}	
}

/*!
 * @brief		Enable the specified USART interrupts
 *
 * @param		usart		Select the USART or the UART peripheral
 *
 * @param		interrupt	specifies the USART interrupt sources to be enabled or disabled
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_EnableInterrupt(USART_T *usart, USART_INT_T interrupt)
{
	uint32_t temp;

	temp = (uint32_t)(interrupt & 0xffff);

	if(interrupt & 0X10000)
	{
		usart->CTRL1 |= temp;
	}

	if(interrupt & 0X20000)
	{
		usart->CTRL2 |= temp;
	}

	if(interrupt & 0X40000)
	{
		usart->CTRL3 |= temp;
	}	
} 

/*!
 * @brief		Enables the USART DMA interface
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @param		mdaReq	specifies the DMA request
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_EnableDMA(USART_T *usart, USART_DMA_T mdaReq)
{
	usart->CTRL3 |= mdaReq;
}

/*!
 * @brief		Disable the USART DMA interface
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @param		mdaReq	specifies the DMA request
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_DisableDMA(USART_T *usart, USART_DMA_T mdaReq)
{
	usart->CTRL3 &= (uint32_t)~mdaReq;
}

/*!
 * @brief		Sets the address of the USART node
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @param		address	Indicates the address of the USART node
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_SetAddress(USART_T* usart, uint8_t address)
{
	usart->CTRL2_B.ADDR = address;
}

/*!
 * @brief		address	Indicates the address of the USART node
 *
 * @param		usart			Select the USART or the UART peripheral
 *
 * @param		wakeupMethod	specifies the USART wakeup method
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_ConfigWakeUp(USART_T* usart, USART_WAKEUP_T wakeupMethod)
{
	usart->CTRL1_B.WUPM = wakeupMethod;
}

/*!
 * @brief		Set USART Receiver in mute mode
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_EnableMuteMode(USART_T* usart)
{
	usart->CTRL1_B.RXWUP = BIT_SET;
}

/*!
 * @brief		Set USART Receiver in active mode
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_DisableMuteMode(USART_T* usart)
{
	usart->CTRL1_B.RXWUP = BIT_RESET;
}

/*!
 * @brief		Sets the USART LIN Break detection length
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @param		LBDL	specifies the LIN break detection length
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_ConfigLINBreakDetectLength(USART_T* usart, USART_LBDL_T LBDL)
{
	usart->CTRL2_B.LBDL = LBDL;
}

/*!
 * @brief		Enables the USART LIN MODE
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_EnableLIN(USART_T* usart)
{
	usart->CTRL2_B.LINEN = BIT_SET;
}

/*!
 * @brief		Disable the USART LIN MODE
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_DisableLIN(USART_T* usart)
{
	usart->CTRL2_B.LINEN = BIT_RESET;
}

/*!
 * @brief		Transmits single data
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @param		data	the data to transmit
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_TxData(USART_T* usart, uint16_t data)
{
	usart->DATA_B.DATA = data;
}

/*!
 * @brief		Returns the most recent received data
 *
 * @param  		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
uint16_t USART_RxData(USART_T* usart)
{
  return (uint16_t)(usart->DATA_B.DATA);
}

/*!
 * @brief		Transmits break characters
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_TxBreak(USART_T* usart)
{
	usart->CTRL1_B.TXBK = BIT_SET;
}

/*!
 * @brief		Sets the specified USART guard time
 *
 * @param		usart		Select the USART or the UART peripheral
 *
 * @param		guardTime	specifies the guard time
 *
 * @retval		None
 *
 * @see 
 *
 * @note 		The guard time bits are not available for UART4 and UART5
 */
void USART_ConfigGuardTime(USART_T* usart, uint8_t guardTime)
{    
	usart->GTDIV_B.GT = guardTime;
}

/*!
 * @brief		Sets the system clock divider number
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @param		div		specifies the divider number
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_ConfigDivNumber(USART_T* usart, uint8_t div)
{ 
	usart->GTDIV_B.DIV = div;
}

/*!
 * @brief		Enables the USART Smart Card mode
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 		The Smart Card mode is not available for UART4 and UART5
 */
void USART_EnableSmartCard(USART_T* usart)
{
	usart->CTRL3_B.SCEN = BIT_SET;
}

/*!
 * @brief		Disable the USART Smart Card mode
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_DisableSmartCard(USART_T* usart)
{
	usart->CTRL3_B.SCEN = BIT_RESET;
}

/*!
 * @brief		Enables NACK transmission
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 		The Smart Card mode is not available for UART4 and UART5
 */
void USART_EnableSmartCardNACK(USART_T* usart)
{
	usart->CTRL3_B.NACKEN = BIT_SET;
}

/*!
 * @brief		Disable NACK transmission
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_DisableSmartCardNACK(USART_T* usart)
{
	usart->CTRL3_B.NACKEN = BIT_RESET;
}


/*!
 * @brief		Enables USART Half Duplex communication
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_EnableHalfDuplex(USART_T* usart)
{
	usart->CTRL3_B.HDEN = BIT_SET;
}

/*!
 * @brief		Disable USART Half Duplex communication
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_DisableHalfDuplex(USART_T* usart)
{
	usart->CTRL3_B.HDEN = BIT_RESET;
}

/*!
 * @brief		Configures the USART's IrDA interface
 *
 * @param		usart	Select the USART or the UART peripheral 
 *
 * @param		IrDAMode	specifies the IrDA mode
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_ConfigIrDA(USART_T* usart, USART_IRDALP_T IrDAMode)
{
	usart->CTRL3_B.IRDALP = IrDAMode;
}

/*!
 * @brief		Enables the USART's IrDA interface
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_EnableIrDA(USART_T* usart)
{
	usart->CTRL3_B.IRDAEN = BIT_SET;
}

/*!
 * @brief		Disable the USART's IrDA interface
 *
 * @param		usart	Select the USART or the UART peripheral
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_DisableIrDA(USART_T* usart)
{
	usart->CTRL3_B.IRDAEN = BIT_RESET;
}

/*!
 * @brief		Read the specified USART flag
 *
 * @param  		usart	Select the USART or the UART peripheral  
 *
 * @param		flag	specifies the flag to check
 *
 * @retval		The new state of flag (SET or RESET)
 *
 * @see 
 *
 * @note 
 */
uint8_t USART_ReadFlag(USART_T* usart, USART_FLAG_T flag)
{
	return (usart->STS & flag) ? SET : RESET;
}

/*!
 * @brief		Clears the USARTx's pending flags
 *
 * @param  		usart	Select the USART or the UART peripheral 
 *
 * @param  		flag	specifies the flag to clear.
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_ClearFlag(USART_T* usart, USART_FLAG_T flag)
{   
	usart->STS &= (uint32_t)~flag;
}

/*!
 * @brief		Read the specified USART interrupt flag
 *
 * @param  		usart	Select the USART or the UART peripheral  
 *
 * @param		intSrc	specifies the USART interrupt source to check
 *
 * @retval		The new state of intSrc (SET or RESET)
 *
 * @see 
 *
 * @note 
 */
uint8_t USART_ReadIntFlag(USART_T* usart, USART_INT_T intSrc)
{
	uint32_t itFlag, srFlag;
	
	if(intSrc & 0x10000)
	{
		itFlag = usart->CTRL1 & intSrc & 0xffff;
	}
	else if(intSrc & 0x20000)
	{
		itFlag = usart->CTRL2 & intSrc & 0xffff;
	}
	else
	{
		itFlag = usart->CTRL3 & intSrc & 0xffff;
	}

	srFlag = intSrc >> 24;
	srFlag = (uint32_t)(1 << srFlag);
	srFlag = usart->STS & srFlag;
	
	if(srFlag && itFlag)
	{
		return SET;
	}

	return RESET;
}

/*!
 * @brief		Clears the USART interrupt pending bits
 *
 * @param  		usart	Select the USART or the UART peripheral 
 *
 * @param		intSrc	specifies the interrupt pending bit to clear
 *
 * @retval		None
 *
 * @see 
 *
 * @note 
 */
void USART_ClearIntFlag(USART_T* usart, USART_INT_T intSrc)
{
	uint32_t srFlag;

	srFlag = intSrc >> 24;
	srFlag = (uint32_t)(1 << srFlag);
	
  	usart->STS &= (uint32_t)~srFlag;
}

