
/******************************************************************************
*@file  : fxx_lpuart.c
*@brief : LPUART SPL module driver.
******************************************************************************/

#include "fxx_std.h"

/******************************************************************************
*@brief : Deinitializes the LPUARTx peripheral registers to their default reset values.
*@param : LPUARTx: where x can be 1.
*@return: None
******************************************************************************/
void LPUART_DeInit(LPUART_TypeDef* LPUARTx)
{
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    
    System_Module_Reset(RST_LPUART);
    System_Module_Disable(EN_LPUART);

}


/******************************************************************************
*@brief : Initialize the LPUART according to the specified.
*         parameters in the LPUART_InitTypeDef.
*@param : LPUARTx: where x can be 1.
*@param : LPUART_InitStruct: pointer to a LPUART_InitTypeDef structure that contains
*                            the configuration information for LPUART module.
*@return: None
******************************************************************************/
void LPUART_Init(LPUART_TypeDef* LPUARTx, LPUART_InitTypeDef *LPUART_InitStruct)
{
    uint32_t temp;
    
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_WORDLENGTH(LPUART_InitStruct->WordLength));
    assert_param(IS_LPUART_STOPBITS(LPUART_InitStruct->StopBits));
    assert_param(IS_LPUART_PARITY(LPUART_InitStruct->Parity));
    assert_param(IS_LPUART_MODE(LPUART_InitStruct->Mode));
    
    
    System_Module_Enable(EN_LPUART);
    System_Module_Reset(RST_LPUART);
    
    /* Set baud rate */
    LPUART_SetBaudRate(LPUARTx, LPUART_InitStruct->BaudRate, LPUART_InitStruct->WordLength, \
                                LPUART_InitStruct->Parity, LPUART_InitStruct->StopBits);
    
    /* Set the communication parameters */
    temp = LPUARTx->LCR;
    temp &= ~(LPUART_LCR_WLEN | LPUART_LCR_STP2 | LPUART_LCR_EPS | LPUART_LCR_SPS | LPUART_LCR_PEN);
    temp |= LPUART_InitStruct->WordLength | LPUART_InitStruct->StopBits | LPUART_InitStruct->Parity;
    LPUARTx->LCR = temp;
    
    /* enable tx rx */
    LPUARTx->CR = (LPUARTx->CR & ~(LPUART_CR_TXE | LPUART_CR_RXE)) | LPUART_InitStruct->Mode;
}

/******************************************************************************
*@brief : Set clock source of the LPUARTx.
*@param : LPUARTx: where x can be 1.
*@param : ClockSource: specifies the clock source of the LPUARTx.
*           This parameter can be one of @ref LPUART_ClockSource.
*             @arg LPUART_CLOCKSOURCE_RC32K: specifies RC32K as the clock source.
*             @arg LPUART_CLOCKSOURCE_XTL: specifies XTL as the clock source.
*             @arg LPUART_CLOCKSOURCE_PCLK_DIV4: specifies the 4 division of PCLK as the clock source.
*             @arg LPUART_CLOCKSOURCE_PCLK_DIV8: specifies the 8 division of PCLK as the clock source.
*             @arg LPUART_CLOCKSOURCE_PCLK_DIV16: specifies the 16 division of PCLK as the clock source.
*             @arg LPUART_CLOCKSOURCE_PCLK_DIV32: specifies the 32 division of PCLK as the clock source.
*@return: None
******************************************************************************/
void LPUART_SetClockSource(LPUART_TypeDef* LPUARTx, uint32_t ClockSource)
{  
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_CLOCKSOURCE(ClockSource));
    
    if(ClockSource == LPUART_CLOCKSOURCE_RC32K)
    {
        System_Enable_RC32K();
    }
    else if(ClockSource == LPUART_CLOCKSOURCE_XTL)
    {
        System_Enable_XTAL();
    }
    
    
    SCU->CCR2 &= ~(SCU_CCR2_LPUCLK_SEL | SCU_CCR2_LPUARTDIV);
    SCU->CCR2 |= ClockSource;
    

}

/******************************************************************************
*@brief : Returns the LPUART clock frequency
*@return: LPUART frequency
******************************************************************************/
uint32_t LPUART_GetCLKFreq(void)
{
    uint32_t source;
    uint32_t div;
    uint32_t freq;
    uint32_t pclk;
    
    
    pclk = System_Get_APBClock();
    
    source = (SCU->CCR2 & (0x3 << 13));
    
    switch(source)
    {
        case SCU_CCR2_LPUART_CLKSEL_RC32K:
            {
                uint32_t tmp = *(uint32_t*)0x8023c;
                float freq_tmp;
                if((uint16_t)((tmp & 0xFFFF0000) >> 16) == (uint16_t)~(tmp & 0xFFFF))
                {
                    freq_tmp = (*(uint32_t*)0x80230 / 1.0285) * 10 + 0.5;                    
                    freq = (uint32_t)freq_tmp;
                }
                else
                {
                    freq = 32768;
                }
                
            }
            break;
        
        case SCU_CCR2_LPUART_CLKSEL_XTL:
            freq = 32768;
            break;
        
        case SCU_CCR2_LPUART_CLKSEL_PLL:  
            div = (SCU->CCR2 & (0x3 << 11));
            switch(div)
            {
                case SCU_CCR2_LPUART_CLK_PLL_DIV4:
                    freq = pclk / 4;
                    break;
                case SCU_CCR2_LPUART_CLK_PLL_DIV8:
                    freq = pclk / 8;
                    break;
                
                case SCU_CCR2_LPUART_CLK_PLL_DIV16:
                    freq = pclk / 16;
                    break;
                
                case SCU_CCR2_LPUART_CLK_PLL_DIV32:
                    freq = pclk / 32;
                    break;
            }
            break;
            
        default:
            assert_param(0);
            break;
    }
    
    return freq;
}

/******************************************************************************
*@brief : Set baud rate of the LPUARTx.
*@param : LPUARTx: where x can be 1. 
*@param : baudRate: specifies the baud rate of the LPUARTx.
*@return: None
******************************************************************************/
void LPUART_SetBaudRate(LPUART_TypeDef* LPUARTx, uint32_t BaudRate, \
                        uint32_t WordLength, uint32_t Parity, uint32_t StopBit)
{

    float tempFloat;
    uint32_t bits;
    uint32_t ClockNum;
    uint32_t iBaudRate;
    const uint16_t fBaudRate[4][12] = 
    {
        {0x0000, 0x0010, 0x0044, 0x0092, 0x00AA, 0x0155, 0x01B6, 0x01DD, 0x01EF, 0x0000, 0x0000, 0x0000},
        {0x0000, 0x0020, 0x0084, 0x0124, 0x014A, 0x02AA, 0x02B5, 0x036D, 0x03DE, 0x03DF, 0x0000, 0x0000},
        {0x0000, 0x0020, 0x0088, 0x0124, 0x0252, 0x02AA, 0x0355, 0x0575, 0x0776, 0x0777, 0x07DF, 0x0000},
        {0x0000, 0x0040, 0x0108, 0x0248, 0x02A4, 0x0554, 0x0AAA, 0x0AEA, 0x0DB6, 0x0EEE, 0x0F7B, 0x0FDF},
    };
    
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_BAUDRATE(BaudRate));
    assert_param(IS_LPUART_WORDLENGTH(WordLength));
    assert_param(IS_LPUART_PARITY(Parity));
    assert_param(IS_LPUART_STOPBITS(StopBit));
    
    /* Calculate the baud rate division factor */
    tempFloat = 0;
    if (LPUARTx == LPUART)
        tempFloat = (float)LPUART_GetCLKFreq();
    tempFloat = tempFloat / BaudRate;
    iBaudRate = (uint32_t)tempFloat;
    tempFloat = tempFloat - iBaudRate;
    iBaudRate--;
    assert_param(IS_LPUART_IBAUD(iBaudRate));
    
    bits = 10;
    if (WordLength != LPUART_WORDLENGTH_8B)
        bits--;
    if (Parity != LPUART_PARITY_NONE)
        bits++;
    if (StopBit != LPUART_STOPBITS_1B)
        bits++;
    
    tempFloat = tempFloat * bits;
    ClockNum = (uint32_t)tempFloat;
    tempFloat = tempFloat - ClockNum;
    if (tempFloat >= 0.5)
        ClockNum++;
    
    LPUARTx->IBAUD = iBaudRate + ((iBaudRate >> 1) << LPUART_IBAUD_RXSAM_Pos);
    LPUARTx->FBAUD = fBaudRate[bits - 9][ClockNum];
}

/******************************************************************************
*@brief : Set Word Length of the LPUARTx.
*@param : LPUARTx: where x can be 1.
*@param : WordLength: specifies the Word Length of the LPUARTx.
*           This parameter can be one of @ref LPUART_WordLength.
*             @arg LPUART_WORDLENGTH_7B: the word length is 7 bits.
*             @arg LPUART_WORDLENGTH_8B: the word length is 8 bits.
*@return: None
******************************************************************************/
void LPUART_SetWordLength(LPUART_TypeDef* LPUARTx, uint32_t WordLength)
{  
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_WORDLENGTH(WordLength));
    
    LPUARTx->LCR = (LPUARTx->LCR & ~LPUART_LCR_WLEN) | WordLength;
}

/******************************************************************************
*@brief : Set Stop bits of the LPUARTx.
*@param : LPUARTx: where x can be 1. 
*@param : StopBit: specifies the Stop bits of the LPUARTx.
*           This parameter can be one of @ref LPUART_StopBits.
*             @arg LPUART_STOPBITS_1B: the Stop bits is 1 bits.
*             @arg LPUART_STOPBITS_2B: the Stop bits is 2 bits.
*@return: None
******************************************************************************/
void LPUART_SetStopBit(LPUART_TypeDef* LPUARTx, uint32_t StopBit)
{  
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_STOPBITS(StopBit));
    
    LPUARTx->LCR = (LPUARTx->LCR & ~LPUART_LCR_STP2) | StopBit;
}

/******************************************************************************
*@brief : Set Parity of the LPUARTx.
*@param : LPUARTx: where x can be 1. 
*@param : Parity: specifies the Parity of the LPUARTx.
*           This parameter can be one of @ref LPUART_Parity.
*             @arg LPUART_PARITY_NONE: no parity.
*             @arg LPUART_PARITY_ODD: 奇校验.
*             @arg LPUART_PARITY_EVEN: 偶校验.
*             @arg LPUART_PARITY_0: 0校验.
*             @arg LPUART_PARITY_1: 1校验.
*@return: None
******************************************************************************/
void LPUART_SetParity(LPUART_TypeDef* LPUARTx, uint32_t Parity)
{  
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_PARITY(Parity));
    
    LPUARTx->LCR = (LPUARTx->LCR & ~(LPUART_LCR_EPS | LPUART_LCR_SPS | LPUART_LCR_PEN)) | Parity;
}

/******************************************************************************
*@brief : Set Wakeup mode of the LPUARTx.
*@param : LPUARTx: where x can be 1. 
*@param : WakeupMode: specifies the Wakeup mode of the LPUARTx.
*           This parameter can be one of @ref LPUART_WakeupMode.
*             @arg LPUART_WAKEUPMODE_NONE: no wakeup.
*             @arg LPUART_WAKEUPMODE_STARTBIT: START bit detection wake-up.
*             @arg LPUART_WAKEUPMODE_ONEBYTENOCHECK: 1 byte data reception is completed, 
*                                                    Parity bit and STOP bit are not checked.
*             @arg LPUART_WAKEUPMODE_ONEBYTECHECK: 1 byte data reception is completed, 
*                                                  Check that the Parity bit and STOP bit are correct.
*             @arg LPUART_WAKEUPMODE_ADDRNOCHECK: Receive data matching succeeded.
*                                                 Parity bit and STOP bit are not checked.
*             @arg LPUART_WAKEUPMODE_ADDRCHECK: Receive data matching succeeded.
*                                               Check that the Parity bit and STOP bit are correct.
*@return: None
******************************************************************************/
void LPUART_SetWakeupMode(LPUART_TypeDef* LPUARTx, uint32_t WakeupMode)
{  
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_WAKEUPMODE(WakeupMode));
    
    LPUARTx->LCR = (LPUARTx->LCR & ~(LPUART_LCR_WKCK | LPUART_LCR_RXWKS)) | WakeupMode;
}

/******************************************************************************
*@brief : Set Wakeup addr of the LPUARTx.
*@param : LPUARTx: where x can be 1.
*@param : WakeupAddr: specifies the Wakeup addr of the LPUARTx.
*                     This parameter must be a number between 0 and 0xFF.
*@return: None
******************************************************************************/
void LPUART_SetWakeupAddr(LPUART_TypeDef* LPUARTx, uint32_t WakeupAddr)
{  
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_WAKEUPADDR(WakeupAddr));
    
    LPUARTx->ADDR = (LPUARTx->ADDR & ~LPUART_ADDR_ADDR) | WakeupAddr;
}

/******************************************************************************
*@brief : Enables or disables sending data polarity reversal.
*@param : LPUARTx: where x can be 1.
*@param : NewState: specifies the new state of sending data polarity reversal.
*                   This parameter can be: ENABLE or DISABLE.
*@return: None
******************************************************************************/
void LPUART_SetReverseTxPolarity(LPUART_TypeDef* LPUARTx, FunctionalState NewState)
{  
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_FUNCTIONAL_STATE(NewState));
    
    if (NewState != DISABLE)
        LPUARTx->CR |= LPUART_LCR_TXPOL;
    else
        LPUARTx->CR &= ~LPUART_LCR_TXPOL;
}

/******************************************************************************
*@brief : Enables or disables received data polarity reversal.
*@param : LPUARTx: where x can be 1. 
*@param : NewState: specifies the new state of received data polarity reversal.
*                   This parameter can be: ENABLE or DISABLE.
*@return: None
******************************************************************************/
void LPUART_SetReverseRxPolarity(LPUART_TypeDef* LPUARTx, FunctionalState NewState)
{  
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_FUNCTIONAL_STATE(NewState));
    
    if (NewState != DISABLE)
        LPUARTx->CR |= LPUART_LCR_RXPOL;
    else
        LPUARTx->CR &= ~LPUART_LCR_RXPOL;
}

/******************************************************************************
*@brief : Enables or disables tx enable bit.
*@param : LPUARTx: where x can be 1.
*@param : NewState: specifies the new state of tx enable bit.
*                   This parameter can be: ENABLE or DISABLE.
*@return: None
******************************************************************************/
void LPUART_TxCmd(LPUART_TypeDef* LPUARTx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_FUNCTIONAL_STATE(NewState));
    
    if (NewState != DISABLE)
        LPUARTx->CR |= LPUART_CR_TXE;
    else
        LPUARTx->CR &= ~LPUART_CR_TXE;
}

/******************************************************************************
*@brief : Enables or disables rx enable bit.
*@param : LPUARTx: where x can be 1. 
*@param : NewState: specifies the new state of rx enable bit.
*                   This parameter can be: ENABLE or DISABLE.
*@return: None
******************************************************************************/
void LPUART_RxCmd(LPUART_TypeDef* LPUARTx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_FUNCTIONAL_STATE(NewState));
    
    if (NewState != DISABLE)
        LPUARTx->CR |= LPUART_CR_RXE;
    else
        LPUARTx->CR &= ~LPUART_CR_RXE;
}

/******************************************************************************
*@brief : Enables or disables dma enable bit.
*@param : LPUARTx: where x can be 1.
*@param : NewState: specifies the new state of dma enable bit.
*                   This parameter can be: ENABLE or DISABLE.
*@return: None
******************************************************************************/
void LPUART_DMACmd(LPUART_TypeDef* LPUARTx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_FUNCTIONAL_STATE(NewState));
    
    if (NewState != DISABLE)
        LPUARTx->CR |= LPUART_CR_DMAEN;
    else
        LPUARTx->CR &= ~LPUART_CR_DMAEN;
}

/******************************************************************************
*@brief : Transmits single data through the LPUARTx peripheral.
*@param : LPUARTx: where x can be 1.
*@param : Data: the data to transmit.
*@return: None
******************************************************************************/
void LPUART_SendData(LPUART_TypeDef* LPUARTx, uint8_t Data)
{
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    
    LPUARTx->TXDR |= Data;
}

/******************************************************************************
*@brief : Returns the most recent received data by the LPUARTx peripheral.
*@param : LPUARTx: where x can be 1.
*@return: The received data.
******************************************************************************/
uint8_t LPUART_ReceiveData(LPUART_TypeDef* LPUARTx)
{
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    
    return (LPUARTx->RXDR & LPUART_RXDR_RXDATA);
}

/******************************************************************************
*@brief : Enables or disables the specified LPUARTx interrupts.
*@param : LPUARTx: where x can be 1.
*@param : IT: specifies the LPUART interrupt sources to be enabled or disabled.
*             This parameter can be any combination of @ref LPUART_WakeupMode.
*               @arg LPUART_IT_START: Start bit detection interrupt.
*               @arg LPUART_IT_MATCH: Address matching interrupt.
*               @arg LPUART_IT_RXOV: Receive override interrupt.
*               @arg LPUART_IT_FE: Receive frame format error (STOP bit) interrupt.
*               @arg LPUART_IT_PE: Receive parity error interrupt.
*               @arg LPUART_IT_TXE: Send buffer empty interrupt .
*               @arg LPUART_IT_TC: Data transmission completion interrupt.
*               @arg LPUART_IT_RX: Byte receiving completion interrupt.
*@param : NewState: new state of the specified LPUARTx interrupts.
*                   This parameter can be: ENABLE or DISABLE.
*@return: None
******************************************************************************/
void LPUART_ITConfig(LPUART_TypeDef* LPUARTx, uint32_t IT, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_ALL_IT(IT));
    assert_param(IS_LPUART_FUNCTIONAL_STATE(NewState));
    
    if (NewState != DISABLE)
        LPUARTx->IE |= IT;
    else
        LPUARTx->IE &= ~IT;
}

/******************************************************************************
*@brief : Checks whether the specified LPUARTx flag is set or not.
*@param : LPUARTx: where x can be 1.
*@param : Flag: specifies the flag to check.
*               This parameter can be one of @ref LPUART_Flags.
*                 @arg LPUART_FLAG_STARTIF: Start bit detection interrupt flag.
*                 @arg LPUART_FLAG_MATCHIF: Address matching interrupt flag.
*                 @arg LPUART_FLAG_TXOVF: TXDR overflow error.
*                 @arg LPUART_FLAG_RXF: Receive buffer full.
*                 @arg LPUART_FLAGR_RXOVIF: Receive override interrupt flag.
*                 @arg LPUART_FLAG_FEIF: Frame format error (STOP) interrupt flag.
*                 @arg LPUART_FLAG_PEIF: Parity error interrupt flag.
*                 @arg LPUART_FLAG_TXEIF: Send buffer empty flag.
*                 @arg LPUART_FLAG_TCIF: Data transmission completion interrupt flag.
*                 @arg LPUART_FLAG_RXIF: Byte receiving completion interrupt flag.
*@return: The new state of USART_FLAG (SET or RESET).
******************************************************************************/
FlagStatus LPUART_GetFlagStatus(LPUART_TypeDef* LPUARTx, uint32_t Flag)
{
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_SINGLE_FLAG(Flag));
    
    if (LPUARTx->SR & Flag)
        return (SET);
    else
        return (RESET);
}

/******************************************************************************
*@brief : Clears the LPUARTx's pending flags
*@param : LPUARTx: where x can be 1.
*@param : Flag: specifies the flag to clear.
*               This parameter can be one of @ref LPUART_Flags.
*                 @arg LPUART_FLAG_STARTIF: Start bit detection interrupt flag.
*                 @arg LPUART_FLAG_MATCHIF: Address matching interrupt flag.
*                 @arg LPUART_FLAG_TXOVF: TXDR overflow error.
*                 @arg LPUART_FLAGR_RXOVIF: Receive override interrupt flag.
*                 @arg LPUART_FLAG_FEIF: Frame format error (STOP) interrupt flag.
*                 @arg LPUART_FLAG_PEIF: Parity error interrupt flag.
*                 @arg LPUART_FLAG_TCIF: Data transmission completion interrupt flag.
*                 @arg LPUART_FLAG_RXIF: Byte receiving completion interrupt flag.
*@return: None
******************************************************************************/
void LPUART_ClearFlag(LPUART_TypeDef* LPUARTx, uint32_t Flag)
{
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_CLEAR_FLAG(Flag));
    
    LPUARTx->SR = Flag;
}

/******************************************************************************
*@brief : Checks whether the specified LPUART interrupt has occurred or not.
*@param : LPUARTx: where x can be 1.
*@param : IT: specifies the LPUART interrupt source to check.
*             This parameter can be any combination of @ref LPUART_WakeupMode.
*               @arg LPUART_IT_START: Start bit detection interrupt.
*               @arg LPUART_IT_MATCH: Address matching interrupt.
*               @arg LPUART_IT_RXOV: Receive override interrupt.
*               @arg LPUART_IT_FE: Receive frame format error (STOP bit) interrupt.
*               @arg LPUART_IT_PE: Receive parity error interrupt.
*               @arg LPUART_IT_TXE: Send buffer empty interrupt .
*               @arg LPUART_IT_TC: Data transmission completion interrupt.
*               @arg LPUART_IT_RX: Byte receiving completion interrupt.
*@return: None
******************************************************************************/
FlagStatus LPUART_GetITStatus(LPUART_TypeDef* LPUARTx, uint32_t IT)
{
    /* Check the parameters */
    assert_param(IS_LPUART_ALL_PERIPH(LPUARTx));
    assert_param(IS_LPUART_SINGLE_IT(IT));
    
    if ((LPUARTx->IE & IT) && (LPUARTx->SR & IT))
        return (SET);
    else
        return (RESET);
}




