/*
  ******************************************************************************
  * @file    fxx_uart.c
  * @version V1.0.0
  * @date    2024 
  * @brief   UART  module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of the Universal Asynchronous Receiver Transmitter Peripheral (UART).
  *           @ Initialization and de-initialization functions
  *           @ IO operation functions
  *           @ Peripheral Control functions
  ******************************************************************************
*/
#include "fxx_std.h"


/* If Use 'UART_MODE_TX_RX_DEBUG', Point to Debug Uart */
UART_TypeDef    *Uart_Debug = NULL;

/* Private function prototypes -----------------------------------------------*/
static void UART_Config_BaudRate(UART_TypeDef* UARTx,UART_InitTypeDef *UART_InitStructure);

/**
  * @brief  Enables or disables the specified UART FIFO(TXFIFO and RXFIFO).
  * @param  UARTx: where x can be 1, 2, 3 or 4 to select the UART peripheral.
  * @param  NewState: new state of the UARTx peripheral.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void UART_FIFOCmd(UART_TypeDef* UARTx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_UART_ALL_INSTANCE(UARTx));
    assert_param(IS_FUNCTIONAL_STATE(NewState));

    if (NewState != DISABLE)
    {
        /* Enable the selected UART by setting the UE bit in the CR1 register */
        UARTx->LCRH |= (UART_LCRH_FEN);
    }
    else
    {
        /* Disable the selected UART by clearing the UE bit in the CR1 register */
        UARTx->LCRH &= ~(UART_LCRH_FEN);
    }
}

/**
  * @brief  Set the specified UART FIFO level for triggering interrupt(TXFIFO and RXFIFO).
  * @param  UARTx: where x can be 1, 2, 3 or 4 to select the UART peripheral.
  * @param  TXFIFO_Level: Specify the TX FIFO level for triggering TXI.
  *           TXFIFO_Level can be one of the following values:
  *             @arg   UART_TX_FIFO_1_16: TXI happen when 0 data   in TXFIFO
  *             @arg   UART_TX_FIFO_1_8:  TXI happen when 2 datas  in TXFIFO
  *             @arg   UART_TX_FIFO_1_4:  TXI happen when 4 datas  in TXFIFO
  *             @arg   UART_TX_FIFO_1_2:  TXI happen when 8 datas  in TXFIFO
  *             @arg   UART_TX_FIFO_3_4:  TXI happen when 12 datas in TXFIFO
  *             @arg   UART_TX_FIFO_7_8:  TXI happen when 14 datas in TXFIFO
  * @note  TXI happened only at the point when the data quantity in TXFIFO changes to one of the values above.
 
  * @param  RXFIFO_Leve: Specify the RX FIFO level for triggering RXI
  *           RXFIFO_Level can be one of the following values:
  *             @arg   UART_RX_FIFO_1_16: RXI happen when 1 data   in RXFIFO
  *             @arg   UART_RX_FIFO_1_8:  RXI happen when 2 datas  in RXFIFO
  *             @arg   UART_RX_FIFO_1_4:  RXI happen when 4 datas  in RXFIFO
  *             @arg   UART_RX_FIFO_1_2:  RXI happen when 8 datas  in RXFIFO
  *             @arg   UART_RX_FIFO_3_4:  RXI happen when 12 datas in RXFIFO
  *             @arg   UART_RX_FIFO_7_8:  RXI happen when 14 datas in RXFIFO
  * @note  RXI happened only at the point when the data quantity in RXFIFO changes to one of the values above.

  * @retval None
  */
void UART_FIFO_Level_Set(UART_TypeDef* UARTx, uint32_t TXFIFO_Level, uint32_t RXFIFO_Level)
{
    /* Check the parameters */
    assert_param(IS_UART_ALL_INSTANCE(UARTx));
    assert_param(IS_UART_TX_FIFO_LEVEL(TXFIFO_Level));
    assert_param(IS_UART_RX_FIFO_LEVEL(RXFIFO_Level));
  
    UARTx->IFLS &= ~(UART_IFLS_RXIFLSEL | UART_IFLS_TXIFLSEL);
  
    UARTx->IFLS |= TXFIFO_Level | RXFIFO_Level;
}

/**
  * @brief  Set the specified UART TX FIFO level for triggering TXI.
  * @param  UARTx: where x can be 1, 2, 3 or 4 to select the UART peripheral.
  * @param  TXFIFO_Level: Specify the TX FIFO level for triggering TXI.
  *           TXFIFO_Level can be one of the following values:
  *             @arg   UART_TX_FIFO_1_16: TXI happen when 0 data   left in TXFIFO
  *             @arg   UART_TX_FIFO_1_8:  TXI happen when 2 datas  left in TXFIFO
  *             @arg   UART_TX_FIFO_1_4:  TXI happen when 4 datas  left in TXFIFO
  *             @arg   UART_TX_FIFO_1_2:  TXI happen when 8 datas  left in TXFIFO
  *             @arg   UART_TX_FIFO_3_4:  TXI happen when 12 datas left in TXFIFO
  *             @arg   UART_TX_FIFO_7_8:  TXI happen when 14 datas left in TXFIFO
  * @note  TXI happened only at the point when the data quantity in TXFIFO changes to one of the values above.
  *
  * @retval None
  */
void UART_TX_FIFO_Level_Set(UART_TypeDef* UARTx, uint32_t TXFIFO_Level)
{
  /* Check the parameters */
    assert_param(IS_UART_ALL_INSTANCE(UARTx));
    assert_param(IS_UART_TX_FIFO_LEVEL(TXFIFO_Level));
  
    UARTx->IFLS &= ~(UART_IFLS_TXIFLSEL);
  
    UARTx->IFLS |= TXFIFO_Level;
}

/**
  * @brief  Set the specified UART RX FIFO level for triggering RXI.
  * @param  UARTx: where x can be 1, 2, 3 or 4 to select the UART peripheral.
  * @param  RXFIFO_Leve: Specify the RX FIFO level for triggering RXI
  *           RXFIFO_Level can be one of the following values:
  *             @arg   UART_RX_FIFO_1_16: RXI happen when 1 data   in RXFIFO
  *             @arg   UART_RX_FIFO_1_8:  RXI happen when 2 datas  in RXFIFO
  *             @arg   UART_RX_FIFO_1_4:  RXI happen when 4 datas  in RXFIFO
  *             @arg   UART_RX_FIFO_1_2:  RXI happen when 8 datas  in RXFIFO
  *             @arg   UART_RX_FIFO_3_4:  RXI happen when 12 datas in RXFIFO
  *             @arg   UART_RX_FIFO_7_8:  RXI happen when 14 datas in RXFIFO
  * @note  RXI happened only at the point when the data quantity in RXFIFO changes to one of the values above.

  * @retval None
  */
void UART_RX_FIFO_Level_Set(UART_TypeDef* UARTx, uint32_t RXFIFO_Level)
{
  /* Check the parameters */
    assert_param(IS_UART_ALL_INSTANCE(UARTx));
    assert_param(IS_UART_RX_FIFO_LEVEL(RXFIFO_Level));
  
    UARTx->IFLS &= ~(UART_IFLS_RXIFLSEL);
  
    UARTx->IFLS |= RXFIFO_Level;
}

/**
  * @brief  Enables or disables the specified UART peripheral.
  * @param  UARTx: where x can be 1, 2 or 3 to select the UART peripheral.
  * @param  NewState: new state of the UARTx peripheral.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void UART_Cmd(UART_TypeDef* UARTx, FunctionalState NewState)
{
    assert_param(IS_UART_ALL_INSTANCE(UARTx));
    assert_param(IS_FUNCTIONAL_STATE(NewState));

    if (NewState != DISABLE)
    {
        /* Enable the selected UART by setting the UARTEN bit in the CR register */
        UARTx->CR |= UART_CR_UARTEN;
    }
    else
    {
        /* Disable the selected USART by clearing the UARTEN bit in the CR register */
        UARTx->CR &= UART_CR_UARTEN;
    }
}

/**
  * @brief  Initialize the UART mode according to the specified parameters 
  *               in the UART_InitTypeDef
  * @param  UARTx: where x can be 1, 2 or 3 to select the UART peripheral.
  * @param  UART_InitStruct: pointer that contains the configuration information 
  *               for the specified UART peripheral.
  * @retval None
  */
void UART_Init(UART_TypeDef* UARTx, UART_InitTypeDef* UART_InitStruct)
{
    assert_param(IS_UART_ALL_INSTANCE(UARTx));
    assert_param(IS_UART_WORD_LENGTH(UART_InitStruct->UART_WordLength));
    assert_param(IS_UART_STOPBITS(UART_InitStruct->UART_StopBits));
    assert_param(IS_UART_PARITY(UART_InitStruct->UART_Parity));
    assert_param(IS_UART_MODE(UART_InitStruct->UART_Mode));
    assert_param(IS_UART_HARDWARE_FLOW_CONTROL(UART_InitStruct->UART_HardwareFlowControl));

    /* Config BaudRate */
    UART_Config_BaudRate(UARTx,UART_InitStruct);
    
    /* Set the UART Communication parameters */
    UARTx->LCRH = UART_InitStruct->UART_WordLength | UART_LCRH_FEN | UART_InitStruct->UART_StopBits | UART_InitStruct->UART_Parity;
    UARTx->CR   = UART_InitStruct->UART_HardwareFlowControl | UART_InitStruct->UART_Mode;
	UARTx->LCRH &= ~UART_LCRH_FEN;
	
    
    if (UART_InitStruct->UART_Mode == UART_MODE_TX_RX_DEBUG) 
    {
        Uart_Debug = UARTx;
    }
    else if (UART_InitStruct->UART_Mode == UART_MODE_HALF_DUPLEX) 
    {
        UARTx->CR2 = UART_CR2_TXOE_SEL | UART_CR2_RX_SEL;
    }

}

/**
  * @brief  Deinitializes the UARTx peripheral registers to their default reset values.
  * @param  UARTx: where x can be 1, 2 or 3 to select the UART peripheral.
  * @retval None
  */
void UART_DeInit(UART_TypeDef* UARTx)
{
    /* Check the parameters */
    assert_param(IS_UART_ALL_INSTANCE(UARTx));

    if (UARTx == UART1)
    {
        System_Module_Reset(RST_UART1);
    }
    else if (UARTx == UART2)
    {
        System_Module_Reset(RST_UART2);
    }
    else if (UARTx == UART3)
    {   
        System_Module_Reset(RST_UART3);
    }    
}

/**
  * @brief  Fills each UART_InitStruct member with its default value.
  * @param  UART_InitStruct: UART_InitTypeDef .
  * @retval None
  */
void UART_StructInit(UART_InitTypeDef* UART_InitStruct)
{
    /* USART_InitStruct members default value */
    UART_InitStruct->UART_BaudRate = 9600;
    UART_InitStruct->UART_WordLength = UART_WORDLENGTH_8B;
    UART_InitStruct->UART_StopBits = UART_STOPBITS_1;
    UART_InitStruct->UART_Parity = UART_PARITY_NONE ;
    UART_InitStruct->UART_Mode = UART_MODE_TX_RX;
    UART_InitStruct->UART_HardwareFlowControl = UART_HWCONTROL_NONE;  
}

/**
  * @brief  Enables or disables the specified USART interrupts.
  * @param  UARTx: Select tthe UART peripheral. This parameter can be one of the following values:
  *               UART1, UART2, UART3.
  * @param  UART_IT: specifies the UART interrupt sources to be enabled or disabled.
  * @param  NewState: ENABLE or DISABLE.
  * @retval None
  */
void UART_ITConfig(UART_TypeDef* UARTx, uint16_t UART_IT, FunctionalState NewState)
{
    assert_param(IS_UART_ALL_INSTANCE(UARTx));
    assert_param(IS_UART_CONFIG_IT(UART_IT));
    assert_param(IS_FUNCTIONAL_STATE(NewState));

    if (NewState != DISABLE)
    {
         UARTx->IE |= UART_IT;
    }
    else
    {
         UARTx->IE &= ~UART_IT;
    }
}  

/**
  * @brief  Clears the UARTx's interrupt pending bits.  
  * @param  UARTx: Select tthe UART peripheral. This parameter can be one of the following values:
  *               UART1, UART2, UART3.
  * @param  UART_IT: specifies the corresponding UART interrupt pending bit to be cleared.
  * @retval None
  */
void UART_ClearITPendingBit(UART_TypeDef* UARTx, uint32_t UART_IT)
{	
    assert_param(IS_UART_ALL_INSTANCE(UARTx));
    assert_param(IS_UART_CLEAR_IT(UART_IT));
	
    UARTx->ICR = UART_IT;
}

/**
  * @brief  Transmits single data through the UARTx peripheral.  
  * @param  UARTx: Select tthe UART peripheral. This parameter can be one of the following values:
  *               UART1, UART2, UART3.
  * @param  Data: the data to transmit.
  * @retval None
  */
void UART_SendData(UART_TypeDef* UARTx, uint8_t Data)
{
    assert_param(IS_UART_ALL_INSTANCE(UARTx));

    UARTx->DR = Data ;

    while(UARTx->FR & UART_FR_BUSY);
}

/**
  * @brief  Receive one data through the UARTx peripheral.  
  * @param  UARTx: Select tthe UART peripheral. This parameter can be one of the following values:
  *               UART1, UART2, UART3.
  * @retval Data received
  */
uint8_t UART_ReceiveData(UART_TypeDef* UARTx)
{
    assert_param(IS_UART_ALL_INSTANCE(UARTx));

    return (uint8_t)UARTx->DR;
}

/******************************************************************************
*@brief : wait Tx Data finished, bus IDLE
*         
*@param :UARTx: Select tthe UART peripheral. This parameter can be one of the following values:
  *               UART1, UART2, UART3.
*@return: None
******************************************************************************/
void UART_Wait_TX_Done(UART_TypeDef *UARTx)
{	
    assert_param (IS_UART_ALL_INSTANCE(UARTx));
    
	/* wait TX not busy*/
	while(READ_BIT(UARTx->FR, UART_FR_BUSY)); 

	return;
}

/**
  * @brief  Checks whether the specified USART interrupt has occurred or not.
  * @param  UARTx: Select tthe UART peripheral. This parameter can be one of the following values:
  *               UART1, UART2, UART3.
  * @param  UART_IT: specifies the corresponding UART interrupt pending bit to be cleared.
  * @retval (SET or RESET).
  */
ITStatus UART_GetITStatus(UART_TypeDef* UARTx, uint16_t UART_IT)
{
    ITStatus bitstatus = RESET;
    
    if(UARTx->MIS & UART_IT)
    {
        bitstatus = SET;
    }
    else
    {
        bitstatus = RESET;
    }

    return bitstatus;  
}


/**
  * @brief  Checks whether the specified UART interrupt status set.
  * @param  UARTx: Select tthe UART peripheral. This parameter can be one of the following values:
  *               UART1, UART2, UART3.
  * @param  UART_IT: specifies the corresponding UART interrupt RIS status.
  * @retval (SET or RESET).
  */
ITStatus UART_GetITRISStatus(UART_TypeDef* UARTx, uint16_t UART_IT)
{
    ITStatus bitstatus = RESET;
    
    if(UARTx->RIS & UART_IT)
    {
        bitstatus = SET;
    }
    else
    {
        bitstatus = RESET;
    }

    return bitstatus;  
}

/**
  * @brief  Enables or disables the UART DMA interface.  
  * @param  UARTx: Select tthe UART peripheral. This parameter can be one of the following values:
  *               UART1, UART2, UART3.
  * @param  UART_DMAReq: specifies the DMA request.
  * @param  NewState: ENABLE or DISABLE.
  * @retval None
  */
void UART_DMACmd(UART_TypeDef* UARTx, uint32_t UART_DMAReq, FunctionalState NewState)
{
	assert_param(IS_UART_ALL_INSTANCE(UARTx));  
	assert_param(IS_FUNCTIONAL_STATE(NewState)); 
	
	if (NewState != DISABLE)
	{
        /* Enable the DMA transfer for selected requests by setting the TXDMAE and/or
         RXDMAE bits in the UART DMACR register */
        UARTx->DMACR |= UART_DMAReq;
	}
	else
	{
        /* Disable the DMA transfer for selected requests by clearing the TXDMAE and/or
         RXDMAE bits in the UART DMACR register */
        UARTx->DMACR &= (uint32_t)~UART_DMAReq;
	}
}


/**
  * @brief  Set the Bit count value and start to count.  
  * @param  UARTx: Select tthe UART peripheral. This parameter can be one of the following values:
  *               UART1, UART2, UART3.
  * @param  BCNT_Value: Bit count value set.
  * @retval None
  */
void UART_BCNT_Start(UART_TypeDef* UARTx, uint8_t BCNT_Value)
{
	assert_param(IS_UART_ALL_INSTANCE(UARTx));
    
    UARTx->BCNT = UART_BCNT_START | BCNT_Value;
}

/**
  * @brief  Config BaudRate
  * @param  UARTx: Select tthe UART peripheral. This parameter can be one of the following values:
  *               UART1, UART2, UART3.
  * @param  UART_InitStruct: pointer that contains the configuration information for the specified UART 
  * @retval None
  */
static void UART_Config_BaudRate(UART_TypeDef* UARTx, UART_InitTypeDef *UART_InitStructure)
{
    uint32_t lu32_PCLK;
    uint32_t lu32_IBAUD, lu32_FBAUD;
    uint64_t lu64_TempValue;

    lu32_PCLK = System_Get_APBClock();

    /* Integral part */
    lu32_IBAUD = lu32_PCLK / (UART_InitStructure->UART_BaudRate * 16);

    /* Fractional part */
    lu64_TempValue = lu32_PCLK % (UART_InitStructure->UART_BaudRate * 16);
    lu64_TempValue = (lu64_TempValue * 1000000) / (UART_InitStructure->UART_BaudRate * 16);
    lu32_FBAUD     = (lu64_TempValue * 64 + 500000) / 1000000;

    if (lu32_FBAUD >= 64) 
    {
        UARTx->IBRD = lu32_IBAUD + 1;
        UARTx->FBRD = 0;
    }
    else 
    {
        UARTx->IBRD = lu32_IBAUD;
        UARTx->FBRD = lu32_FBAUD;
    }
}

#if defined ( __CC_ARM   ) || (defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))
int fputc(int ch, FILE *f)
{
    if (Uart_Debug == NULL) 
    {
        return 0;
    }

    Uart_Debug->DR = ch;
    
    while ((Uart_Debug->FR & UART_FR_BUSY));
    
    return ch;
}
#elif defined (__GNUC__) 

__weak int __io_putchar(int ch)  
{  
    Uart_Debug->DR = ch; 
    while ((Uart_Debug->FR & UART_FR_BUSY));  
    return ch;  
}   

#endif
