/*
  ******************************************************************************
  * @file    HAL_Spi.c
  * @version V1.0.0
  * @date    2020
  * @brief   SPI HAL module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of the Serial Peripheral Interface (SPI) peripheral.
  *           @ Initialization and de-initialization functions
  *           @ IO operation functions
  *           @ Peripheral Control functions
  ******************************************************************************
*/
#include  "ACM32Fxx_HAL.h" 

#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;
/************************************************************************
 * function   : HAL_SPI_IRQHandler
 * Description: This function handles SPI interrupt request.
 * input      : hspi : pointer to a SPI_HandleTypeDef structure that contains
 *                     the configuration information for SPI module
 ************************************************************************/
__weak void HAL_SPI_IRQHandler(SPI_HandleTypeDef *hspi)
{    
    /* 
      NOTE : This function should be modified by the user.
    */    
    if ( (hspi->Instance->STATUS & SPI_STATUS_RX_NOT_EMPTY) && ((hspi->Instance->IE) & SPI_STATUS_RX_NOT_EMPTY) )
    {
        /* In master mode */
        if (hspi->Instance->CTL & SPI_CTL_MST_MODE)
        {
            while (hspi->Instance->STATUS & SPI_STATUS_RX_NOT_EMPTY)
            {
                hspi->Rx_Buffer[hspi->Rx_Count++] = hspi->Instance->DAT;
                
                if (hspi->Rx_Count >= hspi->Rx_Size) 
                {
                    /* Wait Transmit Done */
                    while (!(hspi->Instance->STATUS & SPI_STATUS_RX_BATCH_DONE));
                    
                    /* Clear Batch Done Flag  */
                    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_RX_BATCH_DONE);
                    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);

                    /* Rx Disable */
                    hspi->Instance->RX_CTL &= (~SPI_RX_CTL_EN);

                    /* Receive End */
                    hspi->Instance->CS &= (~SPI_CS_CS0);


                    /* Disable Rx Not Empty Interrupt */
                    CLEAR_BIT(hspi->Instance->IE, SPI_STATUS_RX_NOT_EMPTY);
                    
                    if(hspi->Instance == SPI1)
                        NVIC_ClearPendingIRQ(SPI1_IRQn);
                    else if(hspi->Instance == SPI2)
                        NVIC_ClearPendingIRQ(SPI2_IRQn);
                    
                    /* Clear Batch Done Flag  */
                    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_RX_BATCH_DONE);
                    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);
                    
                    /* Set machine is DILE */
                    hspi->RxState = SPI_RX_STATE_IDLE;
                }
            }
        }
        /* In Slave mode */
        else 
        {
            while ((hspi->Rx_Count < hspi->Rx_Size) && (lu32_ReceiveTimeOut > 0) )   
            {
                if (hspi->Instance->STATUS & SPI_STATUS_RX_NOT_EMPTY) 
                {
                    hspi->Rx_Buffer[hspi->Rx_Count++] = hspi->Instance->DAT;
                    lu32_ReceiveTimeOut = SPI_RX_TIMEOUT;       //If recieve data, Reset the timeout value  
                }
                else
                {
                    lu32_ReceiveTimeOut--;   
                }
                
            }

            /* Rx Disable */
            hspi->Instance->RX_CTL &= (~SPI_RX_CTL_EN);
            
            /* Disable Rx Not Empty Interrupt */
            CLEAR_BIT(hspi->Instance->IE, SPI_STATUS_RX_NOT_EMPTY);
            
            if(hspi->Instance == SPI1)
                NVIC_ClearPendingIRQ(SPI1_IRQn);
            else if(hspi->Instance == SPI2)
                NVIC_ClearPendingIRQ(SPI2_IRQn);
            
            /* Clear Batch Done Flag  */
            SET_BIT(hspi->Instance->STATUS, SPI_STATUS_RX_BATCH_DONE);
            SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);
            
            /* Set machine is DILE */
            hspi->RxState = SPI_RX_STATE_IDLE;
        }
    }
    
    if ( (hspi->Instance->STATUS & SPI_STATUS_TX_FIFO_HALF_EMPTY) && ((hspi->Instance->IE) & SPI_IE_TX_FIFO_HALF_EMPTY_EN) )
    {
        while (hspi->Tx_Count < hspi->Tx_Size)   
        {
            if (!(hspi->Instance->STATUS & SPI_STATUS_TX_FIFO_FULL))
            {
                hspi->Instance->DAT = hspi->Tx_Buffer[hspi->Tx_Count++];
            }
            else
            {
                break;   
            }
            
        }
        /* Clear Tx FIFO half empty Flag  */
        SET_BIT(hspi->Instance->STATUS, SPI_STATUS_TX_FIFO_HALF_EMPTY);
        if(hspi->Tx_Count == hspi->Tx_Size)
        {
            /* Disable Tx FIFO half empty Interrupt  */
            CLEAR_BIT(hspi->Instance->IE, SPI_IE_TX_FIFO_HALF_EMPTY_EN);
        }                
    }
    if ((hspi->Instance->STATUS & SPI_STATUS_TX_BATCH_DONE) && ((hspi->Instance->IE) & SPI_IE_TX_BATCH_DONE_EN) )     
    {
        /* Clear Batch Done Flag  */
        SET_BIT(hspi->Instance->STATUS, SPI_STATUS_TX_BATCH_DONE);
        SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);

        /* Disable TX Batch Done Interrupt */
        CLEAR_BIT(hspi->Instance->IE, SPI_STATUS_TX_BATCH_DONE);
        /* Disable Tx FIFO half empty Interrupt  */
        CLEAR_BIT(hspi->Instance->IE, SPI_IE_TX_FIFO_HALF_EMPTY_EN);
            
        if(hspi->Instance == SPI1)
            NVIC_ClearPendingIRQ(SPI1_IRQn);
        else if(hspi->Instance == SPI2)
            NVIC_ClearPendingIRQ(SPI2_IRQn);
        
        lu32_TX_DMA_TimeOut = SPI_TX_DMA_TIMEOUT;   
        while (hspi->Instance->STATUS & SPI_STATUS_TX_BUSY)
        {
             lu32_TX_DMA_TimeOut--;
             if(0 == lu32_TX_DMA_TimeOut) 
             {
                 break;  
             }
        }

        /* Tx Disable */
        hspi->Instance->TX_CTL &= (~SPI_TX_CTL_EN);  
        hspi->Instance->TX_CTL &= (~SPI_TX_CTL_DMA_REQ_EN);         
        
        if (hspi->Init.SPI_Mode == SPI_MODE_MASTER) 
        {
            /* Transmit End */
            hspi->Instance->CS &= (~SPI_CS_CS0);
        }
        
        /* Tx Disable */
        hspi->Instance->TX_CTL &= (~SPI_TX_CTL_EN);

        hspi->TxState = SPI_TX_STATE_IDLE;
    }

    if ( (hspi->Instance->STATUS & SPI_STATUS_RX_BATCH_DONE) && ((hspi->Instance->IE) & SPI_STATUS_RX_BATCH_DONE) )         
    {
        /* Clear Batch Done Flag  */
        SET_BIT(hspi->Instance->STATUS, SPI_STATUS_RX_BATCH_DONE);
        SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);

        /* Disable RX Batch Done Interrupt */
        CLEAR_BIT(hspi->Instance->IE, SPI_STATUS_RX_BATCH_DONE);

        if(hspi->Instance == SPI1)
            NVIC_ClearPendingIRQ(SPI1_IRQn);
        else if(hspi->Instance == SPI2)
            NVIC_ClearPendingIRQ(SPI2_IRQn);

        /* Rx Disable */
        hspi->Instance->RX_CTL &= (~SPI_RX_CTL_DMA_REQ_EN); 
        hspi->Instance->RX_CTL &= (~SPI_RX_CTL_EN);

        if (hspi->Init.SPI_Mode == SPI_MODE_MASTER) 
        {
            /* Receive End */
            hspi->Instance->CS &= (~SPI_CS_CS0);
        }

        hspi->RxState = SPI_RX_STATE_IDLE;
    }
}

/************************************************************************
 * function   : HAL_SPI_MspInit
 * Description: 
 * input      : hspi : pointer to a SPI_HandleTypeDef structure that contains
 *                     the configuration information for SPI module
 ************************************************************************/
__weak void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
    /* 
      NOTE : This function should be modified by the user.
    */
    
    /* For Example */
    GPIO_InitTypeDef GPIO_Handle; 
    
    /* SPI1 */
    if (hspi->Instance == SPI1)
    {
    }
    /* SPI2 */
    else if (hspi->Instance == SPI2) 
    {
        /* Enable Clock */
        System_Module_Enable(EN_SPI2);
        
        /* SPI2 CS   PB12 */
        /* SPI2 SCK  PB13 */
        /* SPI2 MOSI PB15 */
        /* SPI2 MISO PB14 */
        GPIO_Handle.Pin            = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
        GPIO_Handle.Mode           = GPIO_MODE_AF_PP;
        GPIO_Handle.Pull           = GPIO_PULLUP;
        GPIO_Handle.Alternate      = GPIO_FUNCTION_4;
        HAL_GPIO_Init(GPIOB, &GPIO_Handle);
        
        if (hspi->Init.X_Mode == SPI_4X_MODE) 
        {
            /* SPI2 IO3 PC6 */
            /* SPI2 IO2 PC7 */
            GPIO_Handle.Pin            = GPIO_PIN_6 | GPIO_PIN_7;
            GPIO_Handle.Mode           = GPIO_MODE_AF_PP;
            GPIO_Handle.Pull           = GPIO_PULLUP;
            GPIO_Handle.Alternate       = GPIO_FUNCTION_2;
            HAL_GPIO_Init(GPIOC, &GPIO_Handle);
        }
        
        /* Clear Pending Interrupt */
        NVIC_ClearPendingIRQ(SPI2_IRQn);
        
        /* Enable External Interrupt */
        NVIC_EnableIRQ(SPI2_IRQn);
    }
}

/************************************************************************
 * function   : HAL_SPI_MspDeInit
 * Description: SPI De-Initialize the SPI clock, GPIO, IRQ.
 * input      : hspi : pointer to a SPI_HandleTypeDef structure that contains
 *                     the configuration information for SPI module
 ************************************************************************/
__weak void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi)
{
    /* 
      NOTE : This function should be modified by the user.
    */
    
    /* For Example */
    
    /* SPI1 */
    if (hspi->Instance == SPI1)
    {
    }
    /* SPI2 */
    else if (hspi->Instance == SPI2) 
    {
        /* Disable Clock */
        System_Module_Disable(EN_SPI2);
        
        /* Reset the used GPIO to analog */
        HAL_GPIO_DeInit(GPIOB, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);

        if (hspi->Init.X_Mode == SPI_4X_MODE) 
        {
            HAL_GPIO_DeInit(GPIOC, GPIO_PIN_6 | GPIO_PIN_7);
        }
        
        /* Clear Pending Interrupt */
        NVIC_ClearPendingIRQ(SPI2_IRQn);
        
        /* Disable External Interrupt */
        NVIC_DisableIRQ(SPI2_IRQn);
    }
}

/************************************************************************
 * function   : HAL_SPI_Init 
 * Description: SPI initial with parameters. 
 * input      : hspi : pointer to a SPI_HandleTypeDef structure that contains
 *                     the configuration information for SPI module
 ************************************************************************/
HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi)
{
    /* Check SPI Parameter */
    if (!IS_SPI_ALL_INSTANCE(hspi->Instance))                         return HAL_ERROR;
    if (!IS_SPI_ALL_MODE(hspi->Init.SPI_Mode))                        return HAL_ERROR;
    if (!IS_SPI_WORK_MODE(hspi->Init.SPI_Work_Mode))                  return HAL_ERROR;
    if (!IS_SPI_X_MODE(hspi->Init.X_Mode))                            return HAL_ERROR;
    if (!IS_SPI_FIRST_BIT(hspi->Init.First_Bit))                      return HAL_ERROR;
    if (!IS_SPI_BAUDRATE_PRESCALER(hspi->Init.BaudRate_Prescaler))    return HAL_ERROR;

    /* Init the low level hardware : GPIO, CLOCK, NVIC */
    HAL_SPI_MspInit(hspi);
    
    /* Automatic change direction */
    hspi->Instance->CTL |= (SPI_CTL_IO_MODE);
    
    /* Set SPI Work mode */
    if (hspi->Init.SPI_Mode == SPI_MODE_MASTER)
    {
        SET_BIT(hspi->Instance->CTL, SPI_CTL_MST_MODE);
    }
    else 
    {
        CLEAR_BIT(hspi->Instance->CTL, SPI_CTL_MST_MODE);
        
        hspi->Instance->BATCH = (hspi->Instance->BATCH & (~0x000FFFFFU)) | (1 << 0);
        
        hspi->Instance->TX_CTL |= SPI_TX_CTL_MODE | (0x88 << 8);  // dummy data = 0x88  
        
        if (hspi->Init.X_Mode != SPI_1X_MODE) 
        {
            hspi->Instance->CTL |= SPI_CTL_SFILTER;
        }
        
        /* Slave Alternate Enable */
        hspi->Instance->CTL |= SPI_CTL_SLAVE_EN;
        
        /* Slave Mode Enable Rx By Default */
        hspi->Instance->RX_CTL |= SPI_RX_CTL_EN;
    }

    /* Set SPI First Bit */
    if (hspi->Init.First_Bit == SPI_FIRSTBIT_LSB)
        SET_BIT(hspi->Instance->CTL, SPI_CTL_LSB_FIRST);
    else
        CLEAR_BIT(hspi->Instance->CTL, SPI_CTL_LSB_FIRST);

    /* Set SPI Work Mode */
    hspi->Instance->CTL = ((hspi->Instance->CTL) & (~(SPI_CTL_CPHA | SPI_CTL_CPOL))) | (hspi->Init.SPI_Work_Mode);
    
    /* Set SPI X_Mode */
    hspi->Instance->CTL = ((hspi->Instance->CTL) & (~SPI_CTL_X_MODE)) | (hspi->Init.X_Mode);

    /* Set SPI BaudRate Prescaler */
    hspi->Instance->BAUD = ((hspi->Instance->BAUD) & (~0x0000FFFF)) | (hspi->Init.BaudRate_Prescaler);

    /* Disable All Interrupt */
    hspi->Instance->IE = 0x00000000;

    return HAL_OK;
}

/************************************************************************
 * function   : HAL_SPI_DeInit 
 * Description: De-Initialize the SPI peripheral. 
 * input      : hspi : pointer to a SPI_HandleTypeDef structure that contains
 *                     the configuration information for SPI module
 * return     : HAL_StatusTypeDef : HAL status
 ************************************************************************/
HAL_StatusTypeDef HAL_SPI_DeInit(SPI_HandleTypeDef *hspi)
{
    /* Check the SPI handle allocation */
    if (hspi == NULL)
    {
    return HAL_ERROR;
    }

    /* Check SPI Instance parameter */
    if (!IS_SPI_ALL_INSTANCE(hspi->Instance))   return HAL_ERROR;

    hspi->RxState = SPI_RX_STATE_IDLE;
    hspi->TxState = SPI_TX_STATE_IDLE;

    /* DeInit the low level hardware: GPIO, CLOCK, NVIC... */
    HAL_SPI_MspDeInit(hspi);

    hspi->Rx_Size = 0;
    hspi->Tx_Size = 0;
    hspi->Rx_Count = 0;
    hspi->Tx_Count = 0;

    return HAL_OK;
}

/************************************************************************
 * function   : HAL_SPI_Transmit
 * Description: Transmits an amount of data in blocking mode.
 * input      : hspi  : pointer to a SPI_HandleTypeDef structure that contains
 *                      the configuration information for SPI module
 *              pData : Pointer to data buffer
 *              Size  : Amount of data to be sent
 *              Timeout : Transmit Timeout
 ************************************************************************/
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint32_t Size, uint32_t Timeout)
{
    HAL_StatusTypeDef Status = HAL_OK;
    __IO uint32_t uiTimeout;

    /* Check SPI Parameter */
    if (!IS_SPI_ALL_INSTANCE(hspi->Instance))    return HAL_ERROR;
    if(!Size)    return HAL_ERROR;
    if (pData == NULL)    return HAL_ERROR;
    
    hspi->Tx_Count = 0;
    hspi->Tx_Size = Size;
    hspi->Tx_Buffer = pData;
    
    uiTimeout = Timeout;

    /* Clear Batch Done Flag  */
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_TX_BATCH_DONE);
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);
    
    /* Clear TX FIFO */
    SET_BIT(hspi->Instance->TX_CTL, SPI_TX_CTL_FIFO_RESET);
    CLEAR_BIT(hspi->Instance->TX_CTL, SPI_TX_CTL_FIFO_RESET);
    
    /* Set Data Size */
    hspi->Instance->BATCH = Size;
    
    /* Tx Enable */
    hspi->Instance->TX_CTL |= SPI_TX_CTL_EN;

    if (hspi->Init.SPI_Mode == SPI_MODE_MASTER) 
    {
        /* Transmit Start */
        hspi->Instance->CS |= SPI_CS_CS0;
    }
    else
    {
        /* Rx Disable */
        hspi->Instance->RX_CTL &= (~SPI_RX_CTL_EN);
    }
    
    while(hspi->Tx_Size > 0)
    {
        /* Wait Tx FIFO Not Full */
        while (hspi->Instance->STATUS & SPI_STATUS_TX_FIFO_FULL)
        {
            if(uiTimeout)
            {
                uiTimeout--;
                if (uiTimeout == 0)
                {
                    Status = HAL_TIMEOUT;
                    goto End;
                }
            }
        }        
        hspi->Instance->DAT = hspi->Tx_Buffer[hspi->Tx_Count++];
        hspi->Tx_Size--;
        uiTimeout = Timeout;
    }
    
    if (hspi->Init.SPI_Mode == SPI_MODE_SLAVE) 
    {
        /* Wait Transmit Done */
        while (!(hspi->Instance->STATUS & SPI_STATUS_TX_BUSY));
        while (hspi->Instance->STATUS & SPI_STATUS_TX_BUSY)
        {
            if(uiTimeout)
            {
                uiTimeout--;
                if (uiTimeout == 0)
                {
                    Status = HAL_TIMEOUT;
                    goto End;
                }
            }
        }
    }
    else
    {
        /* Wait Transmit Done */
        while (!(hspi->Instance->STATUS & SPI_STATUS_TX_BATCH_DONE));
        Status = HAL_OK;
    }
    
End:
    /* Clear Batch Done Flag  */
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_TX_BATCH_DONE);
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);

    /* Tx Disable */
    hspi->Instance->TX_CTL &= (~SPI_TX_CTL_EN);
    
    if (hspi->Init.SPI_Mode == SPI_MODE_MASTER)  
    {
        /* Transmit End */
        hspi->Instance->CS &= (~SPI_CS_CS0);
    }

    return Status;
}

/************************************************************************
 * function   : HAL_SPI_Transmit_DMA
 * Description: Transmits an amount of data in blocking mode with DMA.
 * input      : hspi  : pointer to a SPI_HandleTypeDef structure that contains
 *                      the configuration information for SPI module
 *              pData : Pointer to data buffer
 *              Size  : Amount of data to be sent
 ************************************************************************/
HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint32_t Size)
{
    /* Check SPI Parameter */
    if (!IS_SPI_ALL_INSTANCE(hspi->Instance))    return HAL_ERROR;
    
    /* Rx machine is running */
    if (hspi->TxState != SPI_TX_STATE_IDLE) 
    {
        return HAL_ERROR;
    }
    /* Set machine is Sending */
    hspi->TxState = SPI_TX_STATE_SENDING;


    /* Clear Batch Done Flag  */
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_TX_BATCH_DONE);
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);
    
    /* Enable Tx Batch Done Interrupt */
    SET_BIT(hspi->Instance->IE, SPI_STATUS_TX_BATCH_DONE);
    
    /* Set Data Size */
    hspi->Instance->BATCH = Size;
    
    /* Tx FIFO */
    hspi->Instance->TX_CTL &= ~SPI_TX_CTL_DMA_LEVEL;
    hspi->Instance->TX_CTL |= SPI_TX_CTL_DMA_LEVEL_0;    

    /* Tx Enable */
    hspi->Instance->TX_CTL |= SPI_TX_CTL_EN;
    
    if (hspi->Init.SPI_Mode == SPI_MODE_MASTER)
    {
        /* Transmit Start */
        hspi->Instance->CS |= SPI_CS_CS0;
    }
    else
    {
        /* Rx Disable */
        hspi->Instance->RX_CTL &= (~SPI_RX_CTL_EN);
    }
    
    HAL_DMA_Start(hspi->HDMA_Tx, (uint32_t)pData, (uint32_t)&hspi->Instance->DAT, Size);

    hspi->Instance->TX_CTL |= SPI_TX_CTL_DMA_REQ_EN;  
    return HAL_OK;
}

/************************************************************************
 * function   : HAL_SPI_Receive
 * Description: Receive an amount of data in blocking mode.
 * input      : hspi  : pointer to a SPI_HandleTypeDef structure that contains
 *                      the configuration information for SPI module
 *              pData : Pointer to data buffer
 *              Size  : Amount of data to be Receive
 *              Timeout  : Receive Timeout
 ************************************************************************/
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint32_t Size, uint32_t Timeout)
{
    HAL_StatusTypeDef Status = HAL_OK;
    __IO uint32_t uiTimeout;
    
    /* Check SPI Parameter */
    if (!IS_SPI_ALL_INSTANCE(hspi->Instance))    return HAL_ERROR;
    if (pData == NULL)    return HAL_ERROR;
    
    hspi->Rx_Count = 0;
    hspi->Rx_Size = Size;
    hspi->Rx_Buffer = pData;
    uiTimeout = Timeout;
    
    if (hspi->Init.SPI_Mode == SPI_MODE_SLAVE) 
    {
        hspi->Instance->BATCH = 1;
        /* Rx Enable */
        hspi->Instance->RX_CTL |= SPI_RX_CTL_EN;
        
        while ( hspi->Rx_Size > 0) 
        { 
            while (READ_BIT(hspi->Instance->STATUS, SPI_STATUS_RX_FIFO_EMPTY))
            {
                if(uiTimeout)
                {
                    uiTimeout--;
                    if (uiTimeout == 0) 
                    {
                        /* Rx Disable */
                        hspi->Instance->RX_CTL &= (~SPI_RX_CTL_EN);
                        return HAL_TIMEOUT;
                    }
                }
            }           
            
            hspi->Rx_Buffer[hspi->Rx_Count++] = hspi->Instance->DAT;
            hspi->Rx_Size--;
            uiTimeout = Timeout;
        }

        /* Rx Disable */
        hspi->Instance->RX_CTL &= (~SPI_RX_CTL_EN);

        return HAL_OK;
    }

    /* Clear Batch Done Flag  */
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_RX_BATCH_DONE);
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);
    
    /* Set Data Size */
    hspi->Instance->BATCH = Size;
    
    /* Rx Enable */
    hspi->Instance->RX_CTL |= SPI_RX_CTL_EN;
    
    /* Receive Start */
    hspi->Instance->CS |= SPI_CS_CS0;
    
    while(hspi->Rx_Size > 0)
    {
        /* have no timeout */
        if (uiTimeout == 0) 
        {
            /* Wait Rx FIFO Not Empty */
            while (hspi->Instance->STATUS & SPI_STATUS_RX_FIFO_EMPTY);
        }
        else
        {
            while (hspi->Instance->STATUS & SPI_STATUS_RX_FIFO_EMPTY)
            {
                if (uiTimeout-- == 0) 
                {
                    Status = HAL_TIMEOUT;
                    goto End; 
                }
            }
        }
        
        hspi->Rx_Buffer[hspi->Rx_Count++] = hspi->Instance->DAT;
        hspi->Rx_Size--;
    }

    Status = HAL_OK;
    
    /* Wait Transmit Done */
    while (!(hspi->Instance->STATUS & SPI_STATUS_RX_BATCH_DONE));

End:   
    /* Clear Batch Done Flag  */
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_RX_BATCH_DONE);
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);

    /* Rx Disable */
    hspi->Instance->RX_CTL &= (~SPI_RX_CTL_EN);

    /* Receive End */
    hspi->Instance->CS &= (~SPI_CS_CS0);
    
    return Status;
}

/************************************************************************
 * function   : HAL_SPI_Receive_DMA
 * Description: Receive an amount of data in blocking mode with DMA.
 * input      : hspi  : pointer to a SPI_HandleTypeDef structure that contains
 *                      the configuration information for SPI module
 *              pData : Pointer to data buffer
 *              Size  : Amount of data to be Receive
 ************************************************************************/
HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint32_t Size)
{
    /* Check SPI Parameter */
    if (!IS_SPI_ALL_INSTANCE(hspi->Instance))    return HAL_ERROR;
    
    /* Rx machine is running */
    if (hspi->RxState != SPI_RX_STATE_IDLE) 
    {
        return HAL_ERROR;
    }
    /* Set Slave machine is receiving */
    hspi->RxState = SPI_RX_STATE_RECEIVING;


    /* Clear Batch Done Flag  */
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_RX_BATCH_DONE);
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);

    /* Enable Rx Batch Done Interrupt */
    SET_BIT(hspi->Instance->IE, SPI_STATUS_RX_BATCH_DONE);

    /* Set Data Size */
    hspi->Instance->BATCH = Size;

    /* Rx Enable */
    hspi->Instance->RX_CTL |= SPI_RX_CTL_EN;
    /* Rx FIFO */
    hspi->Instance->RX_CTL |= SPI_RX_CTL_DMA_LEVEL_0;

    if (hspi->Init.SPI_Mode == SPI_MODE_MASTER) 
    {
        /* Receive Start */
        hspi->Instance->CS |= SPI_CS_CS0;
    }

    HAL_DMA_Start(hspi->HDMA_Rx, (uint32_t)&hspi->Instance->DAT, (uint32_t)pData, Size);
    
    hspi->Instance->RX_CTL |= SPI_RX_CTL_DMA_REQ_EN; 
    
    return HAL_OK;
}

/************************************************************************
 * function   : HAL_SPI_Wire_Config
 * Description: SPI Wire Config
 * input      : hspi  : pointer to a SPI_HandleTypeDef structure that contains
 *                      the configuration information for SPI module
 *              Mode : This parameter can be a value of @ref X_MODE
 ************************************************************************/
HAL_StatusTypeDef HAL_SPI_Wire_Config(SPI_HandleTypeDef *hspi, uint32_t X_Mode)
{
    /* Check SPI Parameter */
    if (!IS_SPI_ALL_INSTANCE(hspi->Instance))    return HAL_ERROR;
    
    /* Set SPI X_Mode */
    hspi->Instance->CTL = ((hspi->Instance->CTL) & (~SPI_CTL_X_MODE)) | X_Mode;
    
    return HAL_OK;
}
/************************************************************************
 * function   : HAL_SPI_Transmit_IT
 * Description: Transmit an amount of data in blocking mode with interrupt.
 * input      : hspi  : pointer to a SPI_HandleTypeDef structure that contains
 *                      the configuration information for SPI module
 *              pData : Pointer to data buffer
 ************************************************************************/
HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint32_t Size)
{
    /* Check SPI Parameter */
    if (!IS_SPI_ALL_INSTANCE(hspi->Instance))    return HAL_ERROR;
    
    /* Tx machine is running */
    if (hspi->TxState != SPI_TX_STATE_IDLE) 
    {
        return HAL_ERROR;
    }
    
    hspi->Tx_Size   = Size;
    hspi->Tx_Buffer = pData; 
    hspi->Tx_Count = 0;   

    /* Set machine is Sending */
    hspi->TxState = SPI_TX_STATE_SENDING;
    
    /* Clear TX FIFO */
    SET_BIT(hspi->Instance->TX_CTL, SPI_TX_CTL_FIFO_RESET);
    CLEAR_BIT(hspi->Instance->TX_CTL, SPI_TX_CTL_FIFO_RESET);
    
    /* Clear Batch Done Flag  */
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_TX_BATCH_DONE);
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);
    
    /* Set Data Size */
    hspi->Instance->BATCH = Size;

    /* Tx Enable */
    hspi->Instance->TX_CTL |= SPI_TX_CTL_EN;

    if (hspi->Init.SPI_Mode == SPI_MODE_MASTER)
    {
        /* Transmit Start */
        hspi->Instance->CS |= SPI_CS_CS0;
    }
    else
    {
        /* Rx Disable */
        hspi->Instance->RX_CTL &= (~SPI_RX_CTL_EN);
    }
    
    while (hspi->Tx_Count < hspi->Tx_Size)   
    {
        if (!(hspi->Instance->STATUS & SPI_STATUS_TX_FIFO_FULL))
            hspi->Instance->DAT = hspi->Tx_Buffer[hspi->Tx_Count++];
        else
            break;           
    }
    /* Clear Tx FIFO half empty Flag  */
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_TX_FIFO_HALF_EMPTY);


    /* Enable Tx FIFO half empty Interrupt and Tx batch done Interrupt*/
    SET_BIT(hspi->Instance->IE, (SPI_IE_TX_FIFO_HALF_EMPTY_EN | SPI_IE_TX_BATCH_DONE_EN));

    
    return HAL_OK;
}

/************************************************************************
 * function   : HAL_SPI_Receive_IT
 * Description: Receive an amount of data in blocking mode with interrupt.
 * input      : hspi  : pointer to a SPI_HandleTypeDef structure that contains
 *                      the configuration information for SPI module
 *              pData : Pointer to data buffer
 ************************************************************************/
HAL_StatusTypeDef HAL_SPI_Receive_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint32_t Size)
{
    /* Check SPI Parameter */
    if (!IS_SPI_ALL_INSTANCE(hspi->Instance))    return HAL_ERROR;
    
    /* Rx machine is running */
    if (hspi->RxState != SPI_RX_STATE_IDLE) 
    {
        return HAL_ERROR;
    }
    
    /* Set Slave machine is receiving */
    hspi->RxState = SPI_RX_STATE_RECEIVING;
    
    if (hspi->Init.SPI_Mode == SPI_MODE_MASTER)
    {
        /* Clear Batch Done Flag  */
        SET_BIT(hspi->Instance->STATUS, SPI_STATUS_RX_BATCH_DONE);
        SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);
        
        /* Set Data Size */
        hspi->Instance->BATCH = Size;
        
        /* Rx Enable */
        hspi->Instance->RX_CTL |= SPI_RX_CTL_EN;
        
        /* Receive Start */
        hspi->Instance->CS |= SPI_CS_CS0;
    }
    else
    {
        /* Reset BATCH register */
        hspi->Instance->BATCH = 1;
        hspi->Instance->RX_CTL |= SPI_RX_CTL_EN;   
    }

    hspi->Rx_Size   = Size;
    hspi->Rx_Buffer = pData; 
    hspi->Rx_Count = 0;   
    lu32_ReceiveTimeOut = SPI_RX_TIMEOUT;   

    /* Enable Rx FIFO Not Empty Interrupt */
    SET_BIT(hspi->Instance->IE, SPI_STATUS_RX_NOT_EMPTY);
    
    return HAL_OK;
}

/************************************************************************
 * function   : HAL_SPI_TransmitReceive
 * Description: Transmits and recieve an amount of data in blocking mode.
 * input      : hspi  : pointer to a SPI_HandleTypeDef structure that contains
 *                      the configuration information for SPI module
 *              pTxData : Pointer to transmit data buffer
 *              pRxData : Pointer to recieve data buffer
 *              Size  : Amount of data to be sent
 *              Timeout  : TransmitReceive Timeout
 ************************************************************************/
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint32_t Size, uint32_t Timeout)
{
    __IO uint32_t TxFlag = 1U, uiTimeout, uiRegTemp;
    HAL_StatusTypeDef Status = HAL_OK;

    /* Check SPI Parameter */
    if (!IS_SPI_ALL_INSTANCE(hspi->Instance))    return HAL_ERROR;
    if ((pTxData == NULL)||(pRxData == NULL))    return HAL_ERROR;
    
    hspi->Tx_Count = 0;
    hspi->Rx_Count = 0;
    hspi->Tx_Buffer = pTxData;
    hspi->Rx_Buffer = pRxData;
    hspi->Tx_Size = Size;   
    hspi->Rx_Size = Size;   
    uiTimeout = Timeout;
    
    /* Clear Batch Done Flag  */
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_TX_BATCH_DONE);
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);

    /* Tx Enable */
    hspi->Instance->TX_CTL |= SPI_TX_CTL_EN;

    /* Rx Enable */
    hspi->Instance->RX_CTL |= SPI_RX_CTL_EN;

    /* Clear TX FIFO */
    SET_BIT(hspi->Instance->TX_CTL, SPI_TX_CTL_FIFO_RESET);
    CLEAR_BIT(hspi->Instance->TX_CTL, SPI_TX_CTL_FIFO_RESET);
    
        
    if (hspi->Init.SPI_Mode == SPI_MODE_SLAVE) 
    {        
        while((!(hspi->Instance->STATUS & SPI_STATUS_TX_FIFO_FULL)) && (hspi->Tx_Size>0))
        {
            hspi->Instance->DAT = hspi->Tx_Buffer[hspi->Tx_Count++];
            hspi->Tx_Size--;
        }
        
        TxFlag = 0;
    }
    else
    {       
        /* Set Data Size */
        hspi->Instance->BATCH = hspi->Tx_Size;

        /* Transmit Start */
        hspi->Instance->CS |= SPI_CS_CS0;
        TxFlag = 1;
    }
    
    while((hspi->Tx_Size>0) || (hspi->Rx_Size>0))
    {
        uiRegTemp = hspi->Instance->STATUS;
        
        if (hspi->Init.SPI_Mode == SPI_MODE_SLAVE) 
        {
            /* Wait Rx FIFO Not Empty */
            if((!(uiRegTemp & SPI_STATUS_RX_FIFO_EMPTY)) && (hspi->Rx_Size>0))
            {
                hspi->Rx_Buffer[hspi->Rx_Count++] = hspi->Instance->DAT;
                hspi->Rx_Size--;
                TxFlag = 1U;
            }        
            /* Wait Tx FIFO Not Full */
            if(((uiRegTemp & SPI_STATUS_TX_FIFO_HALF_EMPTY)) && (hspi->Tx_Size>0) && (TxFlag == 1U))
            {
                while((!(hspi->Instance->STATUS & SPI_STATUS_TX_FIFO_FULL)) && (hspi->Tx_Size>0))
                {
                    hspi->Instance->DAT = hspi->Tx_Buffer[hspi->Tx_Count++];
                    hspi->Tx_Size--;
                }
                TxFlag = 0;
            }
        }
        else
        {
            /* Wait Tx FIFO Not Full */
            if((!(uiRegTemp & SPI_STATUS_TX_FIFO_FULL)) && (hspi->Tx_Size>0) && (TxFlag == 1U))
            {
                hspi->Instance->DAT = hspi->Tx_Buffer[hspi->Tx_Count++];
                hspi->Tx_Size--;
                TxFlag = 0;
            }
            
            /* Wait Rx FIFO Not Empty */
            if((!(uiRegTemp & SPI_STATUS_RX_FIFO_EMPTY)) && (hspi->Rx_Size>0))
            {
                hspi->Rx_Buffer[hspi->Rx_Count++] = hspi->Instance->DAT;
                hspi->Rx_Size--;
                TxFlag = 1U;
            }
        }
        
        /* Wait Timeout */
        if(uiTimeout)
        {
            uiTimeout--;
            if(uiTimeout == 0)
            {
                Status = HAL_TIMEOUT;
                goto End;
            }
        }
    }        
    /* Wait Transmit Done */
    while (!(hspi->Instance->STATUS & SPI_STATUS_TX_BATCH_DONE));
    
    Status = HAL_OK;

End:    
    /* Clear Batch Done Flag  */
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_TX_BATCH_DONE);
    SET_BIT(hspi->Instance->STATUS, SPI_STATUS_BATCH_DONE);

    /* Tx Disable */
    hspi->Instance->TX_CTL &= (~SPI_TX_CTL_EN);
    
    /* Rx Disable */
    hspi->Instance->RX_CTL &= (~SPI_RX_CTL_EN);

    if (hspi->Init.SPI_Mode == SPI_MODE_MASTER) 
    {
        /* Transmit End */
        hspi->Instance->CS &= (~SPI_CS_CS0);
    }

    return Status;
}

/************************************************************************
 * function   : HAL_SPI_GetTxState
 * Description: Get Tx state.
 * input      : hspi  : pointer to a SPI_HandleTypeDef structure that contains
 *                      the configuration information for SPI module
 ************************************************************************/
uint8_t HAL_SPI_GetTxState(SPI_HandleTypeDef *hspi)
{
    return hspi->TxState;
}

/************************************************************************
 * function   : HAL_SPI_GetRxState
 * Description: Get Rx state.
 * input      : hspi  : pointer to a SPI_HandleTypeDef structure that contains
 *                      the configuration information for SPI module
 ************************************************************************/
uint8_t HAL_SPI_GetRxState(SPI_HandleTypeDef *hspi)
{
    return hspi->RxState;
}


























