
/**
 *******************************************************************************
 *
 * @file        API_Flash.c
 *
 * @brief       Application for serial flash 
 *
 * @par         Project
 *              MG32x02z
 * @version     V1.02
 * @date        2022/09/27
 * @author      Megawin Software Center
 * @copyright   Copyright (c) 2020 Megawin Technology Co., Ltd.
 *              All rights reserved.
 *
 *******************************************************************************
 * @par         Disclaimer 
 * The Demo software is provided "AS IS" without any warranty, either 
 * expressed or implied, including, but not limited to, the implied warranties 
 * of merchantability and fitness for a particular purpose. The author will 
 * not be liable for any special, incidental, consequential or indirect 
 * damages due to loss of data or any other reason. 
 * These statements agree with the world wide and local dictated laws about 
 * authorship and violence against these laws. 
 *******************************************************************************
 *******************************************************************************
 */


/* Includes ------------------------------------------------------------------*/
#include "MG32x02z_Flash_25_API.h"

/* Wizard menu ---------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static SPI_HandleTypeDef    mSPIx;
static DMA_HandleTypeDef    mDMA_TXx;
static DMA_HandleTypeDef    mDMA_RXx;
API_FlashHandlerTypeDef     API_Flash;

static uint8_t API_FLASH_RXBUF[8] = {0};

/* Private function prototypes -----------------------------------------------*/
void API_WriteFlash_Cmd (FunctionalState WREN);
uint16_t API_GetFlashRDCR (void);
void API_Flash_MultiBytesRead_P2P(uint8_t Target, uint32_t Address, uint8_t *BufferAddreass, uint32_t Length);

void DMA_IRQHandler (void);
void SPI0_IRQHandler (void);

/* Exported variables --------------------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/* External vairables --------------------------------------------------------*/


/**
 *******************************************************************************
 * @brief       Control Flash WREN(write enable)
 * @details     
 * @param[in]   WREN: Flash write control
 *  @arg\b      ENABLE:  ENABLE Flash write.
 *  @arg\b      DISABLE: DISABLE Flash write.
 * @return                       
 *******************************************************************************
 */
void API_WriteFlash_Cmd (FunctionalState WREN)
{
    uint8_t TX_BUF[2] = {0};
    uint8_t RX_BUF[2] = {0};


    do
    {
        /* Write enable */
        Flash_nCS = FLASH_nCS_ACTIVE;
        
        if(WREN==ENABLE)
        {
            TX_BUF[0] = FLASH_WRITE_ENABLE;
            MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 1, 10);
            Flash_nCS = FLASH_nCS_NoACTIVE;
            
            /* Read status */
            Flash_nCS = FLASH_nCS_ACTIVE;
            TX_BUF[0] = FLASH_READ_STATUS;
            MID_SPI_TransmitReceive(&mSPIx, &TX_BUF[0], &RX_BUF[0], 2, 10);
            Flash_nCS = FLASH_nCS_NoACTIVE;
        }
        else
        {
            TX_BUF[0] = FLASH_WRITE_DISABLE;
            MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 1, 10);
            Flash_nCS = FLASH_nCS_NoACTIVE;
            return;
        }
        
    /* Check WEL == 1 */
    }while((RX_BUF[1] & 0x02) == 0x00);
}

/**
 *******************************************************************************
 * @brief       Wait program or erase to end. 
 * @details     
 * @return                 
 *******************************************************************************
 */ 
void API_Flash_CheckBusy (void)
{
    uint8_t TDAT, RDAT;
    
    //============================================
    /* Read status register */
    Flash_nCS = FLASH_nCS_ACTIVE;
    TDAT = FLASH_READ_STATUS;
    MID_SPI_Transmit(&mSPIx, &TDAT, 1, 10);
//    Sample_Flash_SetData(1,FLASH_READ_STATUS);

    //===========================================
    /* Check erase or write complete */
    /* Get State */
    do
    {
        MID_SPI_Receive(&mSPIx, &RDAT, 1, 10);
    }while((RDAT & 0x01)==0x01);
    
    Flash_nCS = FLASH_nCS_NoACTIVE; 
}


/**
 *******************************************************************************
 * @brief       Get flash identification
 * @details
 * @return      Flash ID
 * @arg\b           MX25R512
 * @arg\b           MX25R6435
 * @note
 * @par         Example
 * @code
 * @endcode
 *******************************************************************************
 */ 
uint32_t API_Flash_GetID (void)
{
    uint8_t TX_BUF[4]={FLASH_READ_IDENTIFICATION, 0xFF, 0xFF, 0xFF};
    uint8_t RX_BUF[4]={0};
    ctype RDAT;
    
    Flash_nCS = FLASH_nCS_ACTIVE;     
    
    MID_SPI_TransmitReceive(&mSPIx, &TX_BUF[0], &RX_BUF[0], 4, 10);
    Flash_nCS = FLASH_nCS_NoACTIVE;

    RDAT.B[3] = 0;
    RDAT.B[2] = RX_BUF[1];
    RDAT.B[1] = RX_BUF[2];
    RDAT.B[0] = RX_BUF[3];
    
    return RDAT.W ;
}


/**
 *******************************************************************************
 * @brief       API Flash Read Configuration Register
 * @details  
 * @return      Configuration Register 
 * @note
 * @par         Example
 * @code
    if(API_Flsh_QuadEnable_Cmd(ENABLE) != MID_SUCCESS)
 * @endcode
 *******************************************************************************
 */
uint16_t API_GetFlashRDCR (void)
{
    uint8_t TX_BUF[3]={FLASH_READ_CONFIGURATION, 0xFF, 0xFF};
    uint8_t RX_BUF[3]={0};
    ctype RDAT;

    
    Flash_nCS = FLASH_nCS_ACTIVE;
    MID_SPI_TransmitReceive(&mSPIx, &TX_BUF[0], &RX_BUF[0], 3, 10);
    Flash_nCS = FLASH_nCS_NoACTIVE;
    
    RDAT.B[0] = RX_BUF[1];
    RDAT.B[1] = RX_BUF[2];
    
    return(RDAT.H[0]);
}


/**
 *******************************************************************************
 * @brief       API Quad Enable
 * @details  
 * @param[in]   QE
 *  @arg\b          DISABLE
 *  @arg\b          ENABLE
 * @return      None 
 * @note
 * @par         Example
 * @code
    if(API_Flsh_QuadEnable_Cmd(ENABLE) != MID_SUCCESS)
 * @endcode
 *******************************************************************************
 */
MID_StatusTypeDef API_Flsh_QuadEnable_Cmd (FunctionalState QE)
{
    MID_StatusTypeDef State = MID_SUCCESS;
    uint16_t REG;
//    ctype RDAT;
    uint8_t TX_BUF[4] = {0};
    uint8_t RX_BUF[4] = {0};

    /* Enabl */
    API_WriteFlash_Cmd(ENABLE);

    /* Read status */
    TX_BUF[0] = FLASH_READ_STATUS;
    Flash_nCS = FLASH_nCS_ACTIVE;
    MID_SPI_TransmitReceive(&mSPIx, &TX_BUF[0], &RX_BUF[0], 2, 10);
    if(QE == ENABLE)
    {
        TX_BUF[1] = RX_BUF[1] | 0x40;
    }
    else
    {
        TX_BUF[1] = RX_BUF[1] & ~0x40;
    }
//    Sample_Flash_SetData(1,FLASH_READ_STATUS);
//    RDAT.B[0] = Sample_Flash_ReadData();
    Flash_nCS = FLASH_nCS_NoACTIVE;

    /* Read Configuration Register */
    REG = API_GetFlashRDCR();
    TX_BUF[2] = (uint8_t) REG;
    TX_BUF[3] = (uint8_t) (REG >> 8);
    TX_BUF[0] = FLASH_WRITE_STATUS;

    /* Write WDSR */
    Flash_nCS = FLASH_nCS_ACTIVE;
    MID_SPI_TransmitReceive(&mSPIx, &TX_BUF[0], &RX_BUF[0], 4, 10); /* Try */
//    Sample_Flash_SetData(1, FLASH_WRITE_STATUS);
//    Sample_Flash_SetData(3, RDAT.W);
    Flash_nCS = FLASH_nCS_NoACTIVE;

    /* Wait WIP == 0 */
    TX_BUF[0] = FLASH_READ_STATUS;
    do
    {
        Flash_nCS = FLASH_nCS_ACTIVE;
        MID_SPI_TransmitReceive(&mSPIx, &TX_BUF[0], &RX_BUF[0], 2, 10);
//        Sample_Flash_SetData(1, FLASH_READ_STATUS);
//        RDAT.B[0] = Sample_Flash_ReadData();
        Flash_nCS = FLASH_nCS_NoACTIVE;
    }while((RX_BUF[1] & 0x01) != 0);

    Flash_nCS = FLASH_nCS_ACTIVE;
    MID_SPI_TransmitReceive(&mSPIx, &TX_BUF[0], &RX_BUF[0], 2, 10);
//    Sample_Flash_SetData(1,FLASH_READ_STATUS);
//    RDAT.B[0] = Sample_Flash_ReadData();
    Flash_nCS = FLASH_nCS_NoACTIVE;

    // When get status fail
    if((((RX_BUF[1] & 0x40) == 0) && (QE == ENABLE)) ||
       (((RX_BUF[1] & 0x40) != 0) && (QE == DISABLE)))
    {
        State = MID_FAILURE;
    }

    return State;
}

/**
 * @name    Flash 
 *
 */ 
///@{
/**
 *******************************************************************************
 * @brief       Flash Init
 * @details  
 * @param[in]   None
 * @return      None 
 * @note
 * @par         Example
 * @code
    API_Flash_Init ();
 * @endcode
 *******************************************************************************
 */
void API_Flash_Init (void)
{
    mSPIx.Instance = SPI_SPI0;
    
    mSPIx.Init.ClockDivider = SPI_CLOCKDIVIDER_2;
    
    mSPIx.Init.DataLine     = SPI_STANDARD_SPI;
    mSPIx.Init.Mode         = SPI_MODE_MASTER;
    mSPIx.Init.CLKPhase     = SPI_PHASE_1EDGE;
    mSPIx.Init.CLKPolarity  = SPI_POLARITY_LOW;
    mSPIx.Init.FirstBit     = SPI_FIRSTBIT_MSB;
    mSPIx.Init.DataSize     = SPI_DATASIZE_8BIT;
    
    mSPIx.Init.NSSPMode     = SPI_NSS_PULSE_DISABLE;
    mSPIx.Init.NSS          = SPI_NSS_OUT_HARDWARE;
    mSPIx.Init.NSSPMode     = SPI_NSS_PULSE_DISABLE;
    mSPIx.pRxBuffPtr.point8 = &API_FLASH_RXBUF[0];
    
    if(MID_SPI_Init(&mSPIx) != MID_OK)             // When SPI initial failure, stop at here.
    {
        while(1){__NOP();}
    }
    SPI0->CR0.MBIT.RX_CTL = 1;              // SPI RX get data at next edge.
    
    API_Flash.Lock = API_UNLOCKED;
    API_Flash.State = API_FLASH_STATE_READY;
    API_Flash.ErrorCode = MID_SPI_ERROR_NONE;
}
///@} 


/**
 * @name    Flash Erase
 *
 */ 
///@{
/**
 *******************************************************************************
 * @brief       Flash erase
 * @details  
 * @param[in]   Address: start address.
 *  @arg\b          0x00000000 ~ 0x00FFF000. (The address must be aligned to 4K bytes.)
 * @param[in]   ErasePage: 1~n (1 page is 4KByte)
 *  @arg\b          1 ~ 4096.
 * @return
 * @note        Erase minimum block 64K bytes
 *              Maxmum start address is depending on the flash size.
 * @par         Example
 * @code
    API_Flash_Erase (0x00000000, 1);
 * @endcode
 *******************************************************************************
 */
MID_StatusTypeDef API_Flash_Erase (uint32_t Address, uint16_t ErasePage)
{
    uint16_t CNT;
    uint8_t  TX_BUF[4] = {0};



    // API
    if(API_Flash.State == API_FLASH_STATE_READY)
    {
        // Check erase address 4K bytes alignment
        if((Address & 0x00000FFF) != 0)
        {
            return MID_FAILURE;
        }
        
        API_Flash.Lock = API_LOCKED;
        API_Flash.State = API_FLASH_STATE_ERASE;
        API_Flash.Address.W = Address;
        API_Flash.Length = ErasePage;
        
        /* calculate erase the block total number */
       
        for(CNT=0; CNT<ErasePage; CNT++)
        {
            // Eanble flash wirte enable latch
            API_WriteFlash_Cmd(ENABLE);

            // Update erase page address
            TX_BUF[0] = FLASH_ERASE_SECTOR;
            TX_BUF[1] = API_Flash.Address.B[2];
            TX_BUF[2] = API_Flash.Address.B[1];
            TX_BUF[3] = API_Flash.Address.B[0];
            
            // Erase command
            Flash_nCS = FLASH_nCS_ACTIVE;
            MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 4, 10);
            Flash_nCS = FLASH_nCS_NoACTIVE;
        
            // Next erase address
            API_Flash.Address.W += 4096;
            API_Flash.Length--;
        
            // Waitting end
//            API_Flash_CheckBusy();
            
                //============================================
                /* Read status register */
                Flash_nCS = FLASH_nCS_ACTIVE;
                TX_BUF[0] = FLASH_READ_STATUS;
                MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 1, 10);
            //    Sample_Flash_SetData(1,FLASH_READ_STATUS);

                //===========================================
                /* Check erase or write complete */
                /* Get State */
//                MID_SPI_Receive_IT (&mSPIx, &API_FLASH_RXBUF[0], 1);
                
                API_FLASH_RXBUF[0] = FLASH_READ_STATUS;
                do
                {
                    MID_SPI_Receive (&mSPIx, &API_FLASH_RXBUF[0], 1, 10);
                } while((API_FLASH_RXBUF[0] & 0x01) != 0x00);

                // Disable flash
                Flash_nCS = FLASH_nCS_NoACTIVE;
            
        }

        API_Flash.Lock = API_UNLOCKED;
        API_Flash.State = API_FLASH_STATE_READY;

        return MID_SUCCESS;
    }
    else
    {
        return MID_FAILURE;
    }
}
///@} 


/**
 * @name    Flash program
 *
 */ 
///@{
/**
 *******************************************************************************
 * @brief       flash multi-bytes write
 * @details  
 * @param[in]   Address: start address.
 *  @arg\b          0x00000000 ~ 0x00FFFFFF.
 * @param[in]   DataSource: transfer data source address
 *  @arg\b  
 * @param[in]   Length: write total length
 *  @arg\b          0x00000001 ~ 0x00FFFFFF.
 * @return
 * @note        Maxmum length and start address is depending on the flash size.
 * @par         Example
 * @code
    API_Flash_MultiBytesWrite (0x00010000, &source, 0x10000);
 * @endcode
 *******************************************************************************
 */
void API_Flash_MultiBytesWrite (uint32_t Address, uint8_t *DataSource, uint32_t Length)
{
//    ctype AddressIndex;
//    uint32_t Unfinished_Length;
    uint8_t TX_BUF[5] = {0};



    // When API flash is free
    if(API_Flash.State == API_FLASH_STATE_READY)
    {
        API_Flash.Lock = API_LOCKED;
        API_Flash.State = API_FLASH_STATE_PROGRAM;
        API_Flash.Length = Length;
        API_Flash.Address.W = Address;
        API_Flash.pBuffer = DataSource;
        
        // Setting DMA_TX4 data
        mDMA_TXx.Instance = DMA;
        mDMA_TXx.ExtraGPL_Channel = NULL;
        mDMA_TXx.DMAChannelIndex = DMAChannel4;
        mDMA_TXx.Init.BSize = DMA_BSIZE_4BYTE;
        mDMA_TXx.Init.DesPeri = MID_DMA_SPI0_WRITE;
        mDMA_TXx.Init.SrcPeri = MID_DMA_MEMORY_READ;
        mDMA_TXx.Init.MEMMode = DMA_CH0A_CH0_ADSEL_normal_w;
        mDMA_TXx.Init.LoopMode = DMA_LOOP_DISABLE;
        mDMA_TXx.Parent = &mSPIx;
        
//        // Updata unfiniched length
//        Unfinished_Length = Length;
        
        
        
        do{
            // Initial DMA TX setting
            if(MID_DMA_Init(&mDMA_TXx) != MID_OK)
            {
                while(1){__NOP();}
            }
            
            // Eanble flash wirte enable latch
            API_WriteFlash_Cmd(ENABLE);

            NVIC_EnableIRQ(DMA_IRQn);
            mSPIx.mDMATX = &mDMA_TXx;

            // Enable flash
            Flash_nCS = FLASH_nCS_ACTIVE;
            
            TX_BUF[0] = FLASH_QUADPAGE_PROGRAM;

            // Transmit 4PP commnad
            MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 1, 10);
            
            // SPI lines change to QPI data output
            mSPIx.Instance->CR2.B[0] |= SPI_CR2_BDIR_OE_mask_b0;                /* SPI -> QPI output    */
            mSPIx.Instance->CR2.B[0] = (mSPIx.Instance->CR2.B[0] & ~SPI_CR2_DAT_LINE_mask_b0) | SPI_CR2_DAT_LINE_4_b0;
            
            // Transmit program address
            TX_BUF[0] = API_Flash.Address.B[2];
            TX_BUF[1] = API_Flash.Address.B[1];
            TX_BUF[2] = API_Flash.Address.B[0];
            MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 3, 10);
            
            if(API_Flash.Length > 255)
            {    
                // program data
                MID_SPI_Transmit_DMA(&mSPIx, API_Flash.pBuffer, 256);                  /* program length       */
                // Update unfinish length
                API_Flash.pBuffer += 256;
                API_Flash.Address.W += 256;
                API_Flash.Length -= 256;
                
            }
            else
            {
                // program data
                MID_SPI_Transmit_DMA(&mSPIx, API_Flash.pBuffer, API_Flash.Length);    /* program length       */
                // Update unfinish length
                DataSource += API_Flash.Length;
                API_Flash.Address.W += API_Flash.Length;
                API_Flash.Length = 0;
            }
            while(mSPIx.State != MID_SPI_STATE_READY); 
            
            // Disable flash
            Flash_nCS = FLASH_nCS_NoACTIVE;
            
            
            // SPI lines change to SPI data input
            mSPIx.Instance->CR2.B[0] &= ~SPI_CR2_BDIR_OE_mask_b0;               /* QPI output -> SPI    */
            mSPIx.Instance->CR2.B[0] &= ~SPI_CR2_DAT_LINE_mask_b0;
            
            // Waitting end
            // API_Flash_CheckBusy(); 
                //============================================
                /* Read status register */
                Flash_nCS = FLASH_nCS_ACTIVE;
                TX_BUF[0] = FLASH_READ_STATUS;
                MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 1, 10);
            //    Sample_Flash_SetData(1,FLASH_READ_STATUS);

                //===========================================
                /* Check erase or write complete */
                /* Get State */
                
                API_FLASH_RXBUF[0] = FLASH_READ_STATUS;
                do
                {
                    MID_SPI_Receive (&mSPIx, &API_FLASH_RXBUF[0], 1, 10);
                } while((API_FLASH_RXBUF[0] & 0x01) != 0x00);

                // Disable flash
                Flash_nCS = FLASH_nCS_NoACTIVE;
        // Check program end
        }while(API_Flash.Length != 0);
        
        // Clear API lock and state
        API_Flash.Lock = API_UNLOCKED;
        API_Flash.State = API_FLASH_STATE_READY;
    }
}
///@} 


/**
 * @name    Flash read
 *
 */ 
///@{
/**
 *******************************************************************************
 * @brief       SPIx flash multi-bytes write
 * @details  
 * @param[in]   Address: read start address.
 *  @arg\b          0x00000000 ~ 0xFFFFFFFF.
 * @param[in]   BufferAddreass: buffer address.
 *  @arg\b  
 * @param[in]   Length: read total length
 *  @arg\b          0x00000001 ~ 0xFFFFFFFF.
 * @return  
 * @note        Maxmum start address is depending on the flash size.
 * @par         Example
 * @code
    API_Flash_MultiBytesRead (0x00010000, &source, 0x10000);
 * @endcode
 *******************************************************************************
 */
void API_Flash_MultiBytesRead (uint32_t Address, uint8_t *BufferAddreass, uint32_t Length)
{
    ctype AddressIndex;
    uint8_t TX_BUF[5] = {0};

    if(API_Flash.State == API_FLASH_STATE_READY)
    {
        API_Flash.Lock = API_LOCKED;
        API_Flash.State = API_FLASH_STATE_BUSY;
        
        // Initial DMA_RXx
        mDMA_RXx.Instance = DMA;
        mDMA_RXx.ExtraGPL_Channel = NULL;
        mDMA_RXx.DMAChannelIndex = DMAChannel3;
        mDMA_RXx.Init.BSize = DMA_BSIZE_2BYTE;
        mDMA_RXx.Init.DesPeri = MID_DMA_MEMORY_WRITE;
        mDMA_RXx.Init.SrcPeri = MID_DMA_SPI0_READ;
        mDMA_RXx.Init.MEMMode = DMA_CH0A_CH0_ADSEL_normal_w;
        mDMA_RXx.Init.LoopMode = DMA_LOOP_DISABLE;
        mDMA_RXx.Parent = &mSPIx;

        if(MID_DMA_Init(&mDMA_RXx) != MID_OK)
        {
            while(1){__NOP();}
        }
        NVIC_EnableIRQ(DMA_IRQn);
        mSPIx.mDMARX = &mDMA_RXx;

        AddressIndex.W = Address;

        TX_BUF[0] = FLASH_1I_4O_Read;
        TX_BUF[1] = AddressIndex.B[2];
        TX_BUF[2] = AddressIndex.B[1];
        TX_BUF[3] = AddressIndex.B[0];
        TX_BUF[4] = (uint8_t) Flash_Dummy_Data;

        Flash_nCS = FLASH_nCS_ACTIVE;
        // Bidirectional input
        mSPIx.Instance->CR2.B[0] &= ~SPI_CR2_BDIR_OE_mask_b0;

        MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 5, 10);

        // SPI data lines standard SPI change to 4-lines.
        mSPIx.Instance->CR2.B[0] = (mSPIx.Instance->CR2.B[0] & ~SPI_CR2_DAT_LINE_mask_b0) | SPI_CR2_DAT_LINE_4_b0;

        MID_SPI_Receive_DMA(&mSPIx, BufferAddreass, Length);
    }
}

/**
 * @name    Flash read
 *
 */ 
///@{
/**
 *******************************************************************************
 * @brief       SPIx flash multi-bytes write
 * @details  
 * @param[in]   Address: read start address.
 *  @arg\b          0x00000000 ~ 0xFFFFFFFF.
 * @param[in]   BufferAddreass: buffer address.
 *  @arg\b  
 * @param[in]   Length: read total length
 *  @arg\b          0x00000001 ~ 0xFFFFFFFF.
 * @return  
 * @note        Maxmum start address is depending on the flash size.
 * @par         Example
 * @code
    API_Flash_MultiBytesRead (0x00010000, &source, 0x10000);
 * @endcode
 *******************************************************************************
 */
void API_Flash_MultiBytesRead_P2P(uint8_t Target, uint32_t Address, uint8_t *BufferAddreass, uint32_t Length)
{
    ctype AddressIndex;
    uint8_t TX_BUF[5] = {0};

    //=========================================================
    //Prevent unused argument compilcation warning
    ((void)(Target));
    
    
    if(API_Flash.State == API_FLASH_STATE_READY)
    {
        API_Flash.Lock = API_LOCKED;
        API_Flash.State = API_FLASH_STATE_BUSY;
        
        // Initial DMA_RXx
        mDMA_RXx.Instance = DMA;
        mDMA_RXx.ExtraGPL_Channel = NULL;
        mDMA_RXx.DMAChannelIndex = DMAChannel3;
        mDMA_RXx.Init.BSize = DMA_BSIZE_2BYTE;
        mDMA_RXx.Init.DesPeri = MID_DMA_MEMORY_WRITE;
        mDMA_RXx.Init.SrcPeri = MID_DMA_SPI0_READ;
        mDMA_RXx.Init.MEMMode = DMA_CH0A_CH0_ADSEL_normal_w;
        mDMA_RXx.Init.LoopMode = DMA_LOOP_DISABLE;
        mDMA_RXx.Parent = &mSPIx;

        if(MID_DMA_Init(&mDMA_RXx) != MID_OK)
        {
            while(1){__NOP();}
        }
        NVIC_EnableIRQ(DMA_IRQn);
        mSPIx.mDMARX = &mDMA_RXx;

        AddressIndex.W = Address;

        TX_BUF[0] = FLASH_1I_4O_Read;
        TX_BUF[1] = AddressIndex.B[2];
        TX_BUF[2] = AddressIndex.B[1];
        TX_BUF[3] = AddressIndex.B[0];
        TX_BUF[4] = (uint8_t) Flash_Dummy_Data;

        Flash_nCS = FLASH_nCS_ACTIVE;
        // Bidirectional input
        mSPIx.Instance->CR2.B[0] &= ~SPI_CR2_BDIR_OE_mask_b0;

        MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 5, 10);

        // SPI data lines standard SPI change to 4-lines.
        mSPIx.Instance->CR2.B[0] = (mSPIx.Instance->CR2.B[0] & ~SPI_CR2_DAT_LINE_mask_b0) | SPI_CR2_DAT_LINE_4_b0;

        MID_SPI_Receive_DMA(&mSPIx, BufferAddreass, Length);
    }
}

/**
 *******************************************************************************
 * @brief       Tx Transfer completed callback.
 * @details  
 * @param[in]   mSPI:
 *  @arg\b          mSPI. mSPI pointer to a SPI_HandleTypeDef structure that 
 *                  contains the configuration information for SPI module.
 * @return      none
 * @note
 * @par         Example
 * @code
    MID_SPI_TxCpltCallback(mSPI);
 * @endcode
 *******************************************************************************
 */
void DMA_IRQHandler (void)
{
    if((DMA->STA.W & 0x0000000F) != 0)
    {
        if((DMA->CH0B.H[0] & DMA_CH0B_CH0_DET_mask_h0) == MID_DMA_SPI0_WRITE)
        {
            MID_DMA_IRQHandler(&mDMA_TXx);
        }
        
        if((DMA->CH0B.H[0] & DMA_CH0B_CH0_SRC_mask_h0) == MID_DMA_SPI0_READ)
        {
            MID_DMA_IRQHandler(&mDMA_RXx);
        }
    }
    
    if((DMA->STA.W & 0x000000F0) != 0)
    {
        if((DMA->CH1B.H[0] & DMA_CH1B_CH1_DET_mask_h0) == MID_DMA_SPI0_WRITE)
        {
            MID_DMA_IRQHandler(&mDMA_TXx);
        }
        
        if((DMA->CH1B.H[0] & DMA_CH1B_CH1_SRC_mask_h0) == MID_DMA_SPI0_READ)
        {
            MID_DMA_IRQHandler(&mDMA_RXx);
        }
    }
    if((DMA->STA.W & 0x00000F00) != 0)
    {
        if((DMA->CH2B.H[0] & DMA_CH2B_CH2_DET_mask_h0) == MID_DMA_SPI0_WRITE)
        {
            MID_DMA_IRQHandler(&mDMA_TXx);
        }
        
        if((DMA->CH2B.H[0] & DMA_CH2B_CH2_SRC_mask_h0) == MID_DMA_SPI0_READ)
        {
            MID_DMA_IRQHandler(&mDMA_RXx);
        }
    }
    if((DMA->STA.W & 0x0000F000) != 0)
    {
        if((DMA->CH3B.H[0] & DMA_CH3B_CH3_DET_mask_h0) == MID_DMA_SPI0_WRITE)
        {
            MID_DMA_IRQHandler(&mDMA_TXx);
        }
        
        if((DMA->CH3B.H[0] & DMA_CH3B_CH3_SRC_mask_h0) == MID_DMA_SPI0_READ)
        {
            MID_DMA_IRQHandler(&mDMA_RXx);
        }
    }
    if((DMA->STA.W & 0x000F0000) != 0)
    {
        if((DMA->CH4B.H[0] & DMA_CH4B_CH4_DET_mask_h0) == MID_DMA_SPI0_WRITE)
        {
            MID_DMA_IRQHandler(&mDMA_TXx);
        }
        
        if((DMA->CH4B.H[0] & DMA_CH4B_CH4_SRC_mask_h0) == MID_DMA_SPI0_READ)
        {
            MID_DMA_IRQHandler(&mDMA_RXx);
        }
    }
}


/**
 *******************************************************************************
 * @brief       Tx Transfer completed callback.
 * @details  
 * @param[in]   mSPI:
 *  @arg\b          mSPI. mSPI pointer to a SPI_HandleTypeDef structure that 
 *                  contains the configuration information for SPI module.
 * @return      none
 * @note
 * @par         Example
 * @code
    MID_SPI_TxCpltCallback(mSPI);
 * @endcode
 *******************************************************************************
 */
void MID_SPI_TxCpltCallback(SPI_HandleTypeDef *mSPI)
{
    UNUSED(mSPI);
    // To do ...
}


/**
 *******************************************************************************
 * @brief       Rx Transfer completed callback.
 * @details  
 * @param[in]   mSPI:
 *  @arg\b          mSPI. mSPI pointer to a SPI_HandleTypeDef structure that 
 *                  contains the configuration information for SPI module.
 * @return      none
 * @note
 * @par         Example
 * @code
    MID_SPI_RxCpltCallback(mSPI);
 * @endcode
 *******************************************************************************
 */
void MID_SPI_RxCpltCallback(SPI_HandleTypeDef *mSPI)
{
    UNUSED(mSPI);
    Flash_nCS = FLASH_nCS_NoACTIVE;
    // SPI data lines standard SPI change to 4-lines.
    mSPIx.Instance->CR2.B[0] = mSPIx.Instance->CR2.B[0] & ~SPI_CR2_DAT_LINE_mask_b0;
    if(mSPIx.State == MID_SPI_STATE_READY)
    {
        API_Flash.State = API_FLASH_STATE_READY;
    }
    else
    {
       API_Flash.State = API_FLASH_STATE_BUSY; 
    }
    API_Flash.Lock = API_UNLOCKED;
}


/**
 *******************************************************************************
 * @brief       Tx and Rx Transfer completed callback.
 * @details  
 * @param[in]   mSPI:
 *  @arg\b          mSPI. mSPI pointer to a SPI_HandleTypeDef structure that 
 *                  contains the configuration information for SPI module.
 * @return      none
 * @note
 * @par         Example
 * @code
    MID_SPI_TxRxCpltCallback(mSPI);
 * @endcode
 *******************************************************************************
 */
void MID_SPI_TxRxCpltCallback(SPI_HandleTypeDef *mSPI)
{
    UNUSED(mSPI);
    // To do ...
}


void SPI0_IRQHandler (void)
{
    uint8_t TX_BUF[8]={0};

    API_FlashHandlerTypeDef API_Store;

    /* Store state and lock */
    API_Store.State = API_Flash.State;
    API_Store.Lock = API_Flash.Lock;
    
    mSPIx.pRxBuffPtr.point8 = &API_FLASH_RXBUF[0];
    MID_SPI_IRQHandler(&mSPIx);
    
    if(mSPIx.RxXferCount == 0)
    {
        /* Reload state and lock */
        API_Flash.State = API_Store.State;
        API_Flash.Lock = API_Store.Lock;
        
        /* When API flash run erase operation */
        if(API_Flash.State == API_FLASH_STATE_ERASE)
        {
            // When not in write operation
            if((API_FLASH_RXBUF[0] & FLASH_StatusRegister_WIP) == 0)
            {
                // Check flash WIP end signal
                Flash_nCS = FLASH_nCS_NoACTIVE; 
                
                // When erase not complete
                if(API_Flash.Length != 0)
                {
                    /* Call erase 4K code */
                    API_WriteFlash_Cmd(ENABLE);

                    // Update erase page address
                    TX_BUF[0] = FLASH_ERASE_SECTOR;
                    TX_BUF[1] = API_Flash.Address.B[2];
                    TX_BUF[2] = API_Flash.Address.B[1];
                    TX_BUF[3] = API_Flash.Address.B[0];
                    
                    // Erase command
                    Flash_nCS = FLASH_nCS_ACTIVE;
                    MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 4, 10);
                    Flash_nCS = FLASH_nCS_NoACTIVE;
                
                    // Next erase address
                    API_Flash.Address.W += 4096;
                    // Erase time -1
                    API_Flash.Length--;
                    
    //                // Waitting end
    //                API_Flash_CheckBusy();
                        //============================================
                        /* Read status register */
                        Flash_nCS = FLASH_nCS_ACTIVE;
                        TX_BUF[0] = FLASH_READ_STATUS;
                        MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 1, 10);
                    //    Sample_Flash_SetData(1,FLASH_READ_STATUS);

                        //===========================================
                        /* Check erase or write complete */
                        /* Get State */
//                        MID_SPI_Receive_IT(&mSPIx, (uint8_t*)mSPIx.pRxBuffPtr.point8, 1);
                        MID_SPI_Receive_IT(&mSPIx, &API_FLASH_RXBUF[0], 1);
                }
                
                // When erase complete
                else
                {
                    API_Flash.Lock = API_UNLOCKED;
                    API_Flash.State = API_FLASH_STATE_READY;
                }
            }
//            // When write operation 
//            else
//            {
//                MID_SPI_Receive_IT(&mSPIx, mSPIx.pRxBuffPtr, 1);
//            }
        }
        /* When API flash run program operation */
        else if(API_Flash.State == API_FLASH_STATE_PROGRAM)
        {
            // When not in write operation
            if((API_FLASH_RXBUF[0] & FLASH_StatusRegister_WIP) == 0)
            {
                // Check flash WIP end signal
                Flash_nCS = FLASH_nCS_NoACTIVE; 
                
                // When program not complete
                if(API_Flash.Length != 0)
                {
                    /* Call program code */
                    if(MID_DMA_Init(&mDMA_TXx) != MID_OK)
                    {
                        while(1){__NOP();}
                    }
                    
                    // Eanble flash wirte enable latch
                    API_WriteFlash_Cmd(ENABLE);

                    NVIC_EnableIRQ(DMA_IRQn);
                    mSPIx.mDMATX = &mDMA_TXx;

                    // Enable flash
                    Flash_nCS = FLASH_nCS_ACTIVE;
                    
                    TX_BUF[0] = FLASH_QUADPAGE_PROGRAM;

                    // Transmit 4PP commnad
                    MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 1, 10);
                    
                    // SPI lines change to QPI data output
                    mSPIx.Instance->CR2.B[0] |= SPI_CR2_BDIR_OE_mask_b0;                /* SPI -> QPI output    */
                    mSPIx.Instance->CR2.B[0] = (mSPIx.Instance->CR2.B[0] & ~SPI_CR2_DAT_LINE_mask_b0) | SPI_CR2_DAT_LINE_4_b0;
                    
                    // Transmit program address
                    TX_BUF[0] = API_Flash.Address.B[2];
                    TX_BUF[1] = API_Flash.Address.B[1];
                    TX_BUF[2] = API_Flash.Address.B[0];
                    MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 3, 10);
                    
                    if(API_Flash.Length > 255)
                    {    
                        // program data
                        MID_SPI_Transmit_DMA(&mSPIx, API_Flash.pBuffer, 256);                  /* program length       */
                        // Update unfinish length
                        API_Flash.pBuffer += 256;
                        API_Flash.Address.W += 256;
                        API_Flash.Length -= 256;
                        
                    }
                    else
                    {
                        // program data
                        MID_SPI_Transmit_DMA(&mSPIx, API_Flash.pBuffer, API_Flash.Length);    /* program length       */
                        // Update unfinish length
                        API_Flash.pBuffer += API_Flash.Length;
                        API_Flash.Address.W += API_Flash.Length;
                        API_Flash.Length = 0;
                    }
                    while(mSPIx.State != MID_SPI_STATE_READY); 
                    
                    // Disable flash
                    Flash_nCS = FLASH_nCS_NoACTIVE;
                    
                    
                    // SPI lines change to SPI data input
                    mSPIx.Instance->CR2.B[0] &= ~SPI_CR2_BDIR_OE_mask_b0;               /* QPI output -> SPI    */
                    mSPIx.Instance->CR2.B[0] &= ~SPI_CR2_DAT_LINE_mask_b0;
                    
    //                // Waitting end
    //                API_Flash_CheckBusy(); 
                    //============================================
                        /* Read status register */
                        Flash_nCS = FLASH_nCS_ACTIVE;
                        TX_BUF[0] = FLASH_READ_STATUS;
                        MID_SPI_Transmit(&mSPIx, &TX_BUF[0], 1, 10);
                    //    Sample_Flash_SetData(1,FLASH_READ_STATUS);

                        //===========================================
                        /* Check erase or write complete */
                        /* Get State */
//                        MID_SPI_Receive_IT(&mSPIx, (uint8_t*)mSPIx.pRxBuffPtr.point8, 1);
                        MID_SPI_Receive_IT(&mSPIx, &API_FLASH_RXBUF[0], 1);
                }
                // WHen program complete
                else
                {
                    API_Flash.Lock = API_UNLOCKED;
                    API_Flash.State = API_FLASH_STATE_READY;
                }
            }
//            // When write operation
//            else
//            {
//                MID_SPI_Receive_IT(&mSPIx, mSPIx.pRxBuffPtr, 1);
//            }
        }
        
    }
}


///**
// *******************************************************************************
// * @brief       SysTick Handler
// * @details  
// * @param[in]   mSPI:
// *  @arg\b          mSPI. mSPI pointer to a SPI_HandleTypeDef structure that 
// *                  contains the configuration information for SPI module.
// * @return      none
// * @note
// * @par         Example
// * @code
//    MID_SPI_TxCpltCallback(mSPI);
// * @endcode
// *******************************************************************************
// */
//void SysTick_Handler (void)
//{
//    if((API_Flash.State == API_FLASH_STATE_ERASE) | (API_Flash.State == API_FLASH_STATE_PROGRAM))
//    {
//        if((mSPIx.RxXferCount == 0) & (mSPIx.TxXferCount == 0))
//        {
//            MID_SPI_Receive_IT(&mSPIx, mSPIx.pRxBuffPtr, 1);
//        }
//    }
//}
