#include "bsp_spi.h"

#define SPI_BSP    SPI2
#define SPI_BSP_IRQ    SPI2_IRQn

DMA_InitTypeDef    spi_dma_rx;          /* SPI Rx DMA inition parameters */
DMA_InitTypeDef    spi_dma_tx;          /* SPI Tx DMA inition parameters */
GPIO_InitTypeDef   spi_gpio_init;
SPI_InitTypeDef    spi_init;
SPI_DataTypeDef    spi_data;

#define SPI_RX_TIMEOUT    2000
#define SPI_TX_DMA_TIMEOUT    2000
volatile uint32_t lu32_ReceiveTimeOut = SPI_RX_TIMEOUT;
volatile uint32_t lu32_TX_DMA_TimeOut = SPI_TX_DMA_TIMEOUT;

//void Spi_Gpio_Init(void)
//{
//    //Enable GPIO Clock
//    System_Module_Enable(EN_GPIOAB);
//    
//    /* SPI3 CS   PortA Pin15 */
//    /* SPI3 CLK  PortC Pin10 */
//    /* SPI3 MOSI PortC Pin12 */
//    /* SPI3 MISO PortC Pin11 */
//    spi_gpio_init.Pin            = GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
//    spi_gpio_init.Mode           = GPIO_MODE_AF_PP;
//    spi_gpio_init.Pull           = GPIO_PULLUP;
//    spi_gpio_init.Alternate      = GPIO_FUNCTION_3;
//    GPIO_Init(GPIOC, &spi_gpio_init);

//    spi_gpio_init.Pin            = GPIO_PIN_15;
//    spi_gpio_init.Mode           = GPIO_MODE_AF_PP;
//    spi_gpio_init.Pull           = GPIO_PULLUP;
//    spi_gpio_init.Alternate      = GPIO_FUNCTION_5;
//    GPIO_Init(GPIOA, &spi_gpio_init);
//    
//    /* SPI3 IO3 PortC Pin8 */
//    /* SPI3 IO2 PortC Pin9 */
//    spi_gpio_init.Pin            = GPIO_PIN_8 | GPIO_PIN_9;
//    spi_gpio_init.Mode           = GPIO_MODE_AF_PP;
//    spi_gpio_init.Pull           = GPIO_PULLUP;
//    spi_gpio_init.Alternate       = GPIO_FUNCTION_3;
//    GPIO_Init(GPIOC, &spi_gpio_init);
//}

void Spi_Init(void)
{
    /* Enable Clock */
    System_Module_Enable(EN_SPI2);

    spi_init.SPI_Mode               = SPI_MODE_MASTER;
    spi_init.SPI_CPOL               = SPI_CPOL_HIGH;
    spi_init.SPI_CPHA               = SPI_CPHA_2EDGE;
    spi_init.SPI_X_Mode             = SPI_1X_MODE;
    spi_init.SPI_FirstBit           = SPI_FIRSTBIT_MSB;
    spi_init.SPI_BaudRatePrescaler  = SPI_BAUDRATE_PRESCALER_32;

    SPI_Init(SPI_BSP, &spi_init);

    /* Clear Pending Interrupt */
    NVIC_ClearPendingIRQ(SPI_BSP_IRQ);
    
    /* Enable External Interrupt */
    NVIC_EnableIRQ(SPI_BSP_IRQ);
}

/************************************************************************
 * function   : SPI_Transmit
 * Description: Transmits an amount of data in blocking mode.
 * input      : pData : Pointer to data buffer
 *              Size  : Amount of data to be sent
 *              Timeout : Transmit Timeout
 ************************************************************************/
SPI_StatusTypeDef SPI_Transmit(uint8_t *pData, uint32_t Size, uint32_t Timeout)
{
    uint32_t i;
    SPI_StatusTypeDef Status = SPI_OK;
    __IO uint32_t uiTimeout;

    /* Check SPI Parameter */
    if(!Size)    return SPI_ERROR;
    if (pData == NULL)    return SPI_ERROR;
    
    spi_data.Tx_Count = 0;
    spi_data.Tx_Size = Size;
    spi_data.Tx_Buffer = pData;
    
    uiTimeout = Timeout;

    /* Clear Batch Done Flag  */
    SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);
    
    /* Clear TX FIFO */
    SPI_ClearTxFifo(SPI_BSP);
    
    /* Set Data Size */
    SPI_WriteBatch(SPI_BSP, Size);
    
    /* Tx Enable */
    SPI_TxCmd(SPI_BSP, ENABLE);

    if (spi_init.SPI_Mode == SPI_MODE_MASTER) 
    {
        /* Transmit Start */
        SPI_SSOutputCmd(SPI_BSP, ENABLE);
    }
    else
    {
        /* Rx Disable */
        SPI_RxCmd(SPI_BSP, DISABLE);
    }
    
    while(spi_data.Tx_Size > 0)
    {
        /* Wait Tx FIFO Not Full */
        while(SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_FIFO_FULL) == SET)
        {
            if(uiTimeout)
            {
                uiTimeout--;
                if (uiTimeout == 0)
                {
                    Status = SPI_TIMEOUT;
                    goto End;
                }
            }
        }  
        SPI_SendData(SPI_BSP, spi_data.Tx_Buffer[spi_data.Tx_Count++]);
        spi_data.Tx_Size--;
        uiTimeout = Timeout;
    }
    
    if (spi_init.SPI_Mode == SPI_MODE_SLAVE) 
    {
        /* Wait Transmit Done */
        while(SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_BUSY) == RESET);
        while(SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_BUSY) == SET)
        {
            if(uiTimeout)
            {
                uiTimeout--;
                if (uiTimeout == 0)
                {
                    Status = SPI_TIMEOUT;
                    goto End;
                }
            }
        }
    }
    else
    {
        
        /* Wait Transmit Done */
        while (SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_BATCH_DONE) == RESET);
        Status = SPI_OK;
    }
    
End:
    /* Clear Batch Done Flag  */
    SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);

    /* Tx Disable */
    SPI_TxCmd(SPI_BSP, DISABLE);
    
    if (spi_init.SPI_Mode == SPI_MODE_MASTER)  
    {
        /* Transmit End */
        SPI_SSOutputCmd(SPI_BSP, DISABLE);
    }

    return Status;
}

/************************************************************************
 * function   : SPI_Receive
 * Description: Receive an amount of data in blocking mode.
 * input      : pData : Pointer to data buffer
 *              Size  : Amount of data to be Receive
 *              Timeout  : Receive Timeout
 ************************************************************************/
SPI_StatusTypeDef SPI_Receive(uint8_t *pData, uint32_t Size, uint32_t Timeout)
{
    uint32_t i;
    SPI_StatusTypeDef Status = SPI_OK;
    __IO uint32_t uiTimeout;
    
    /* Check SPI Parameter */
    if (pData == NULL)    return SPI_ERROR;
    
    spi_data.Rx_Count = 0;
    spi_data.Rx_Size = Size;
    spi_data.Rx_Buffer = pData;
    uiTimeout = Timeout;
    
    if (spi_init.SPI_Mode == SPI_MODE_SLAVE) 
    {
        SPI_WriteBatch(SPI_BSP, 1);
        /* Rx Enable */
        SPI_RxCmd(SPI_BSP, ENABLE);
        
        while ( spi_data.Rx_Size > 0) 
        { 
            while(SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_RX_FIFO_EMPTY) == SET)
            {
                if(uiTimeout)
                {
                    uiTimeout--;
                    if (uiTimeout == 0) 
                    {
                        /* Rx Disable */
                        SPI_RxCmd(SPI_BSP, DISABLE);
                        return SPI_TIMEOUT;
                    }
                }
            }           
            
            spi_data.Rx_Buffer[spi_data.Rx_Count++] = SPI_ReceiveData(SPI_BSP);
            spi_data.Rx_Size--;
            uiTimeout = Timeout;
        }

        /* Rx Disable */
        SPI_RxCmd(SPI_BSP, DISABLE);

        return SPI_OK;
    }

    /* Clear Batch Done Flag  */
    SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);
    
    /* Set Data Size */
    SPI_WriteBatch(SPI_BSP, Size);
    
    /* Rx Enable */
    SPI_RxCmd(SPI_BSP, ENABLE);
    
    /* Receive Start */
    SPI_SSOutputCmd(SPI_BSP, ENABLE);
    
    while(spi_data.Rx_Size > 0)
    {
        /* have no timeout */
        if (uiTimeout == 0) 
        {
            /* Wait Rx FIFO Not Empty */
            while(SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_RX_FIFO_EMPTY) == SET);
        }
        else
        {
            while(SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_RX_FIFO_EMPTY) == SET)
            {
                if (uiTimeout-- == 0) 
                {
                    Status = SPI_TIMEOUT;
                    goto End; 
                }
            }
        }
        
        spi_data.Rx_Buffer[spi_data.Rx_Count++] = SPI_ReceiveData(SPI_BSP);
        spi_data.Rx_Size--;
    }

    Status = SPI_OK;
    
    /* Wait Transmit Done */
    while(SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_RX_BATCH_DONE) == RESET);

End:   
    /* Clear Batch Done Flag  */
    SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);

    /* Rx Disable */
    SPI_RxCmd(SPI_BSP, DISABLE);

    /* Receive End */
    SPI_SSOutputCmd(SPI_BSP, DISABLE);
    
    return Status;
}

/************************************************************************
 * function   : SPI_Transmit_DMA
 * Description: Transmits an amount of data in blocking mode with DMA.
 * input      : pData : Pointer to data buffer
 *              Size  : Amount of data to be sent
 ************************************************************************/
SPI_StatusTypeDef SPI_Transmit_DMA(uint8_t *pData, uint32_t Size)
{    
    /* DMA channel1 configuration */
    DMA_DeInit(DMA_Channel1);
    spi_dma_tx.Request_ID       = REQ3_SPI2_SEND;
    spi_dma_tx.SrcAddr = (uint32_t)pData;
    spi_dma_tx.DstAddr = (uint32_t)&SPI2->DAT;
    spi_dma_tx.Data_Flow = DMA_DATA_FLOW_M2P;
    spi_dma_tx.DMA_BufferSize = Size;
    spi_dma_tx.Source_Inc       = DMA_SOURCE_ADDR_INCREASE_ENABLE;
    spi_dma_tx.Desination_Inc   = DMA_DST_ADDR_INCREASE_DISABLE;
    spi_dma_tx.Source_Width     = DMA_SRC_WIDTH_BYTE;
    spi_dma_tx.Desination_Width = DMA_DST_WIDTH_BYTE;
    spi_dma_tx.DMA_Mode = DMA_NORMAL;

    DMA_Init(DMA_Channel1, &spi_dma_tx);
    
    /* Tx machine is running */
    if (spi_data.TxState != SPI_TX_STATE_IDLE) 
    {
        return SPI_ERROR;
    }
    /* Set machine is Sending */
    spi_data.TxState = SPI_TX_STATE_SENDING;


    /* Clear Batch Done Flag  */
    SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);
    
    /* Enable Tx Batch Done Interrupt */
    SPI_ITConfig(SPI_BSP, SPI_IE_TX_BATCH_DONE_EN, ENABLE);
    
    /* Set Data Size */
    SPI_WriteBatch(SPI_BSP, Size);
    
    /* Clear Tx FIFO */
    SPI_ClearTxFifo(SPI_BSP);
    
    SPI_BSP->TX_CTL &= ~SPI_TX_CTL_DMA_LEVEL;
    SPI_BSP->TX_CTL |= SPI_TX_CTL_DMA_LEVEL_0;

    /* Tx Enable */
    SPI_TxCmd(SPI_BSP, ENABLE);
    
    if (spi_init.SPI_Mode == SPI_MODE_MASTER)
    {
        /* Transmit Start */
        SPI_SSOutputCmd(SPI_BSP, ENABLE);
    }
    else
    {
        /* Rx Disable */
        SPI_RxCmd(SPI_BSP, DISABLE);
    }
    
    /* Enable DMA channel1 transfer */
    DMA_Cmd(DMA_Channel1, ENABLE);

    SPI_DMACmd(SPI_BSP, SPI_DMAReq_TX, ENABLE);
    
    return SPI_OK;
}

/************************************************************************
 * function   : SPI_Receive_DMA
 * Description: Receive an amount of data in blocking mode with DMA.
 * input      : pData : Pointer to data buffer
 *              Size  : Amount of data to be Receive
 ************************************************************************/
SPI_StatusTypeDef SPI_Receive_DMA(uint8_t *pData, uint32_t Size)
{    
    /* DMA channel2 configuration */
    DMA_DeInit(DMA_Channel2);
    spi_dma_rx.Request_ID       = REQ4_SPI2_RECV;
    spi_dma_rx.SrcAddr = (uint32_t)&SPI2->DAT;
    spi_dma_rx.DstAddr = (uint32_t)pData;
    spi_dma_rx.Data_Flow = DMA_DATA_FLOW_P2M;
    spi_dma_rx.DMA_BufferSize = Size;
    spi_dma_rx.Source_Inc       = DMA_SOURCE_ADDR_INCREASE_DISABLE;
    spi_dma_rx.Desination_Inc   = DMA_DST_ADDR_INCREASE_ENABLE;
    spi_dma_rx.Source_Width     = DMA_SRC_WIDTH_BYTE;
    spi_dma_rx.Desination_Width = DMA_DST_WIDTH_BYTE;
    spi_dma_rx.DMA_Mode = DMA_NORMAL;

    DMA_Init(DMA_Channel2, &spi_dma_rx);

    /* Rx machine is running */
    if (spi_data.RxState != SPI_RX_STATE_IDLE) 
    {
        return SPI_ERROR;
    }
    /* Set Slave machine is receiving */
    spi_data.RxState = SPI_RX_STATE_RECEIVING;


    /* Clear Batch Done Flag  */
    SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);

    /* Enable Rx Batch Done Interrupt */
    SPI_ITConfig(SPI_BSP, SPI_IE_RX_BATCH_DONE_EN, ENABLE);

    /* Set Data Size */
    SPI_WriteBatch(SPI_BSP, Size);

    /* Rx Enable */
    SPI_RxCmd(SPI_BSP, ENABLE);
    /* Rx FIFO */
    SPI_WireDmaFifo(SPI_BSP, SPI_RX_CTL_DMA_LEVEL_0);

    if (spi_init.SPI_Mode == SPI_MODE_MASTER) 
    {
        /* Receive Start */
        SPI_SSOutputCmd(SPI_BSP, ENABLE);
    }
    
    /* Enable DMA Channel2 transfer */
    DMA_Cmd(DMA_Channel2, ENABLE);

    SPI_DMACmd(SPI_BSP, SPI_DMAReq_RX, ENABLE);
  
    return SPI_OK;
}

/************************************************************************
 * function   : SPI_Transmit_IT
 * Description: Transmit an amount of data in blocking mode with interrupt.
 * input      : pData : Pointer to data buffer
 *              Size  : Amount of data to be Transmit
 ************************************************************************/
SPI_StatusTypeDef SPI_Transmit_IT(uint8_t *pData, uint32_t Size)
{    
    /* Tx machine is running */
    if (spi_data.TxState != SPI_TX_STATE_IDLE) 
    {
        return SPI_ERROR;
    }
    
    spi_data.Tx_Size   = Size;
    spi_data.Tx_Buffer = pData; 
    spi_data.Tx_Count = 0;   

    /* Set machine is Sending */
    spi_data.TxState = SPI_TX_STATE_SENDING;
    
    /* Clear Tx FIFO */
    SPI_ClearTxFifo(SPI_BSP);
    
    /* Clear Batch Done Flag  */
    SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);
    
    /* Set Data Size */
    SPI_WriteBatch(SPI_BSP, Size);

    /* Tx Enable */
    SPI_TxCmd(SPI_BSP, ENABLE);

    if (spi_init.SPI_Mode == SPI_MODE_MASTER)
    {
        /* Transmit Start */
        SPI_SSOutputCmd(SPI_BSP, ENABLE);
    }
    else
    {
        /* Rx Disable */
        SPI_RxCmd(SPI_BSP, DISABLE);
    }
    
    while (spi_data.Tx_Count < spi_data.Tx_Size)   
    {
        if (SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_FIFO_FULL) == RESET)
            SPI_SendData(SPI_BSP, spi_data.Tx_Buffer[spi_data.Tx_Count++]);
        else
            break;           
    }


    /* Enable Tx FIFO half empty Interrupt and Tx batch done Interrupt*/
    SPI_ITConfig(SPI_BSP, (SPI_IE_TX_FIFO_HALF_EMPTY_EN | SPI_IE_TX_BATCH_DONE_EN), ENABLE);
    
    return SPI_OK;
}

/************************************************************************
 * function   : SPI_Receive_IT
 * Description: Receive an amount of data in blocking mode with interrupt.
 * input      : pData : Pointer to data buffer
 *              Size  : Amount of data to be Receive
 ************************************************************************/
SPI_StatusTypeDef SPI_Receive_IT(uint8_t *pData, uint32_t Size)
{    
    /* Rx machine is running */
    if (spi_data.RxState != SPI_RX_STATE_IDLE) 
    {
        return SPI_ERROR;
    }
    
    /* Set Slave machine is receiving */
    spi_data.RxState = SPI_RX_STATE_RECEIVING;
    
    if (spi_init.SPI_Mode == SPI_MODE_MASTER)
    {
        /* Clear Batch Done Flag  */
        SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);
        
        /* Set Data Size */
        SPI_WriteBatch(SPI_BSP, Size);
        
        /* Rx Enable */
        SPI_RxCmd(SPI_BSP, ENABLE);
        
        /* Receive Start */
        SPI_SSOutputCmd(SPI_BSP, ENABLE);
    }
    else
    {
        /* Reset BATCH register */
        SPI_WriteBatch(SPI_BSP, 1);
        SPI_RxCmd(SPI_BSP, ENABLE);   
    }
    
    spi_data.Rx_Size   = Size;
    spi_data.Rx_Buffer = pData; 
    spi_data.Rx_Count = 0;   
    lu32_ReceiveTimeOut = SPI_RX_TIMEOUT;   

    /* Enable Rx FIFO Not Empty Interrupt */
    SPI_ITConfig(SPI_BSP, SPI_IE_RX_NOT_EMPTY_EN, ENABLE);
    
    return SPI_OK;
}

/************************************************************************
 * function   : SPI_TransmitReceive
 * Description: Transmits and recieve an amount of data in blocking mode.
 * input      : pTxData : Pointer to transmit data buffer
 *              pRxData : Pointer to recieve data buffer
 *              Size  : Amount of data to be sent
 *              Timeout  : TransmitReceive Timeout
 ************************************************************************/
SPI_StatusTypeDef SPI_TransmitReceive(uint8_t *pTxData, uint8_t *pRxData, uint32_t Size, uint32_t Timeout)
{
    uint32_t i;
    __IO uint32_t TxFlag = 1U, uiTimeout;
    SPI_StatusTypeDef Status = SPI_OK;

    /* Check SPI Parameter */
    if ((pTxData == NULL)||(pRxData == NULL))    return SPI_ERROR;
    
    spi_data.Tx_Count = 0;
    spi_data.Rx_Count = 0;
    spi_data.Tx_Buffer = pTxData;
    spi_data.Rx_Buffer = pRxData;
    spi_data.Tx_Size = Size;   
    spi_data.Rx_Size = Size;   
    uiTimeout = Timeout;
    
    /* Clear Batch Done Flag  */
    SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);

    /* Tx Enable */
    SPI_TxCmd(SPI_BSP, ENABLE);

    /* Rx Enable */
    SPI_RxCmd(SPI_BSP, ENABLE);

    /* Clear TX FIFO */
    SPI_ClearTxFifo(SPI_BSP);
        
    if (spi_init.SPI_Mode == SPI_MODE_SLAVE) 
    {        
        while((SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_FIFO_FULL) == RESET) && (spi_data.Tx_Size>0))
        {
            SPI_SendData(SPI_BSP, spi_data.Tx_Buffer[spi_data.Tx_Count++]);
            spi_data.Tx_Size--;
        }
        TxFlag = 0;
    }
    else
    {       
        /* Set Data Size */
        SPI_WriteBatch(SPI_BSP, spi_data.Tx_Size);

        /* Transmit Start */
        SPI_SSOutputCmd(SPI_BSP, ENABLE);
        TxFlag = 1;
    }
    
    while((spi_data.Tx_Size>0) || (spi_data.Rx_Size>0))
    {
        if (spi_init.SPI_Mode == SPI_MODE_SLAVE) 
        {
            /* Wait Rx FIFO Not Empty */
            if((SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_RX_FIFO_EMPTY) == RESET) && (spi_data.Rx_Size>0))
            {
                spi_data.Rx_Buffer[spi_data.Rx_Count++] = SPI_ReceiveData(SPI_BSP);
                spi_data.Rx_Size--;
                TxFlag = 1U;
            }        
            /* Wait Tx FIFO Not Full */
            if((SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_FIFO_FULL) == RESET) && (spi_data.Tx_Size>0) && (TxFlag == 1U))
            {
                while((SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_FIFO_FULL) == RESET) && (spi_data.Tx_Size>0))
                {
                    SPI_SendData(SPI_BSP, spi_data.Tx_Buffer[spi_data.Tx_Count++]);
                    spi_data.Tx_Size--;
                }
                TxFlag = 0;
            }
        }
        else
        {
            /* Wait Tx FIFO Not Full */
            if((SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_FIFO_FULL) == RESET) && (spi_data.Tx_Size>0) && (TxFlag == 1U))
            {
                SPI_SendData(SPI_BSP, spi_data.Tx_Buffer[spi_data.Tx_Count++]);
                spi_data.Tx_Size--;
                TxFlag = 0;
            }
            
            /* Wait Rx FIFO Not Empty */
            if((SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_RX_FIFO_EMPTY) == RESET) && (spi_data.Rx_Size>0))
            {
                spi_data.Rx_Buffer[spi_data.Rx_Count++] = SPI_ReceiveData(SPI_BSP);
                spi_data.Rx_Size--;
                TxFlag = 1U;
            }
        }
        
        /* Wait Timeout */
        if(uiTimeout)
        {
            uiTimeout--;
            if(uiTimeout == 0)
            {
                Status = SPI_TIMEOUT;
                goto End;
            }
        }
    }        
    /* Wait Transmit Done */
    while(SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_BATCH_DONE) == RESET);
    
    Status = SPI_OK;

End:    
    /* Clear Batch Done Flag  */
    SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);

    /* Tx Disable */
    SPI_TxCmd(SPI_BSP, DISABLE);
    
    /* Rx Disable */
    SPI_RxCmd(SPI_BSP, DISABLE);

    if (spi_init.SPI_Mode == SPI_MODE_MASTER) 
    {
        /* Transmit End */
        SPI_SSOutputCmd(SPI_BSP, DISABLE);
    }

    return Status;
}

/************************************************************************
 * function   : SPI_GetTxState
 * Description: Get Tx state.
 * input      : 
 ************************************************************************/
uint8_t SPI_GetTxState(void)
{
    return spi_data.TxState;
}

/************************************************************************
 * function   : SPI_GetRxState
 * Description: Get Rx state.
 * input      : 
 ************************************************************************/
uint8_t SPI_GetRxState(void)
{
    return spi_data.RxState;
}

/************************************************************************
 * function   : SPI_IRQProcess
 * Description: SPI IRQ process function.
 ************************************************************************/
void SPI_IRQProcess(void)
{
    uint8_t lu8_TempValue = 0;
    
    uint32_t lu32_Length = 0;
    
    /* 
      NOTE : This function should be modified by the user.
    */    
    if ((SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_RX_NOT_EMPTY) == SET) && (SPI_ITGet(SPI_BSP, SPI_IE_RX_NOT_EMPTY_EN) == SET))
    {
        /* In master mode */
        if (spi_init.SPI_Mode == SPI_MODE_MASTER) 
        {
            while (SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_RX_NOT_EMPTY) == SET)
            {
                spi_data.Rx_Buffer[spi_data.Rx_Count++] = SPI_ReceiveData(SPI_BSP);
                
                if (spi_data.Rx_Count >= spi_data.Rx_Size) 
                {
                    /* Wait Transmit Done */
                    while (SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_RX_BATCH_DONE) == RESET);
                    
                    
                    /* Clear Batch Done Flag  */
                    SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);

                    /* Rx Disable */
                    SPI_RxCmd(SPI_BSP, DISABLE);

                    /* Receive End */
                    SPI_SSOutputCmd(SPI_BSP, DISABLE);


                    /* Disable Rx Not Empty Interrupt */
                    SPI_ITConfig(SPI_BSP, SPI_IE_RX_NOT_EMPTY_EN, DISABLE);

                    NVIC_ClearPendingIRQ(SPI_BSP_IRQ);
                    
                    /* Clear Batch Done Flag  */
                    SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);
                    
                    /* Set machine is DILE */
                    spi_data.RxState = SPI_RX_STATE_IDLE;
                }
            }
        }
        /* In Slave mode */
        else 
        {
            while ((spi_data.Rx_Count < spi_data.Rx_Size) && (lu32_ReceiveTimeOut > 0) )   
            {
                if (SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_RX_NOT_EMPTY) == SET)
                {
                    spi_data.Rx_Buffer[spi_data.Rx_Count++] = SPI_ReceiveData(SPI_BSP);
                    lu32_ReceiveTimeOut = SPI_RX_TIMEOUT;       //If recieve data, Reset the timeout value  
                }
                else
                {
                    lu32_ReceiveTimeOut--;   
                }
                
            }

            /* Rx Disable */
            SPI_RxCmd(SPI_BSP, DISABLE);
            
            /* Disable Rx Not Empty Interrupt */
            SPI_ITConfig(SPI_BSP, SPI_IE_RX_NOT_EMPTY_EN, DISABLE);

            NVIC_ClearPendingIRQ(SPI_BSP_IRQ);
            
            /* Clear Batch Done Flag  */
            SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);
            
            /* Set machine is DILE */
            spi_data.RxState = SPI_RX_STATE_IDLE;
        }
    }
    
    if ((SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_FIFO_HALF_EMPTY) == SET) && (SPI_ITGet(SPI_BSP, SPI_IE_TX_FIFO_HALF_EMPTY_EN) == SET))
    {
        while (spi_data.Tx_Count < spi_data.Tx_Size)   
        {
            if (SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_FIFO_FULL) == RESET)
            {
                SPI_SendData(SPI_BSP, spi_data.Tx_Buffer[spi_data.Tx_Count++]);
            }
            else
            {
                break;   
            }
            
        }
        /* Clear Tx FIFO half empty Flag  */
        if(spi_data.Tx_Count == spi_data.Tx_Size)
        {
            /* Disable Tx FIFO half empty Interrupt  */
            SPI_ITConfig(SPI_BSP, SPI_IE_TX_FIFO_HALF_EMPTY_EN, DISABLE);
        }                
    }
    if ((SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_BATCH_DONE) == SET) && (SPI_ITGet(SPI_BSP, SPI_IE_TX_BATCH_DONE_EN) == SET))
    {
        /* Clear Batch Done Flag  */
        SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);

        /* Disable TX Batch Done Interrupt */
        SPI_ITConfig(SPI_BSP, SPI_STATUS_TX_BATCH_DONE, DISABLE);
        /* Disable Tx FIFO half empty Interrupt  */
        SPI_ITConfig(SPI_BSP, SPI_IE_TX_FIFO_HALF_EMPTY_EN, DISABLE);

        NVIC_ClearPendingIRQ(SPI_BSP_IRQ);

        lu32_TX_DMA_TimeOut = SPI_TX_DMA_TIMEOUT;   
        while((SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_TX_BUSY) == SET))
        {
             lu32_TX_DMA_TimeOut--;
             if(0 == lu32_TX_DMA_TimeOut) 
             {
                 break;  
             }
        }

        /* Tx Disable */
        SPI_TxCmd(SPI_BSP, DISABLE);  
        SPI_DMACmd(SPI_BSP, SPI_DMAReq_TX, DISABLE); 
        
        if (spi_init.SPI_Mode == SPI_MODE_MASTER) 
        {
            /* Transmit End */
            SPI_SSOutputCmd(SPI_BSP, DISABLE);
        }

        spi_data.TxState = SPI_TX_STATE_IDLE;
    }

    if ((SPI_GetFlagStatus(SPI_BSP, SPI_STATUS_RX_BATCH_DONE) == SET) && (SPI_ITGet(SPI_BSP, SPI_IE_RX_BATCH_DONE_EN) == SET))
    {
        /* Clear Batch Done Flag  */
        SPI_ClearFlag(SPI_BSP, SPI_CLEAR_BATCH_DONE);

        /* Disable RX Batch Done Interrupt */
        SPI_ITConfig(SPI_BSP, SPI_IE_RX_BATCH_DONE_EN, DISABLE);

        NVIC_ClearPendingIRQ(SPI_BSP_IRQ);

        /* Rx Disable */
        SPI_DMACmd(SPI_BSP, SPI_DMAReq_RX, DISABLE); 
        SPI_RxCmd(SPI_BSP, DISABLE);

        if (spi_init.SPI_Mode == SPI_MODE_MASTER) 
        {
            /* Receive End */
            SPI_SSOutputCmd(SPI_BSP, DISABLE);
        }

        spi_data.RxState = SPI_RX_STATE_IDLE;
    }
}
