/**
*     Copyright (c) 2023, Nations Technologies Inc.
*
*     All rights reserved.
*
*     This software is the exclusive property of Nations Technologies Inc. (Hereinafter
* referred to as NATIONS). This software, and the product of NATIONS described herein
* (Hereinafter referred to as the Product) are owned by NATIONS under the laws and treaties
* of the People's Republic of China and other applicable jurisdictions worldwide.
*
*     NATIONS does not grant any license under its patents, copyrights, trademarks, or other
* intellectual property rights. Names and brands of third party may be mentioned or referred
* thereto (if any) for identification purposes only.
*
*     NATIONS reserves the right to make changes, corrections, enhancements, modifications, and
* improvements to this software at any time without notice. Please contact NATIONS and obtain
* the latest version of this software before placing orders.

*     Although NATIONS has attempted to provide accurate and reliable information, NATIONS assumes
* no responsibility for the accuracy and reliability of this software.
*
*     It is the responsibility of the user of this software to properly design, program, and test
* the functionality and safety of any application made of this information and any resulting product.
* In no event shall NATIONS be liable for any direct, indirect, incidental, special,exemplary, or
* consequential damages arising in any way out of the use of this software or the Product.
*
*     NATIONS Products are neither intended nor warranted for usage in systems or equipment, any
* malfunction or failure of which may cause loss of human life, bodily injury or severe property
* damage. Such applications are deemed, "Insecure Usage".
*
*     All Insecure Usage shall be made at user's risk. User shall indemnify NATIONS and hold NATIONS
* harmless from and against all claims, costs, damages, and other liabilities, arising from or related
* to any customer's Insecure Usage.

*     Any express or implied warranty with regard to this software or the Product, including,but not
* limited to, the warranties of merchantability, fitness for a particular purpose and non-infringement
* are disclaimed to the fullest extent permitted by law.

*     Unless otherwise explicitly permitted by NATIONS, anyone may not duplicate, modify, transcribe
* or otherwise distribute this software for any purposes, in whole or in part.
*
*     NATIONS products and technologies shall not be used for or incorporated into any products or systems
* whose manufacture, use, or sale is prohibited under any applicable domestic or foreign laws or regulations.
* User shall comply with any applicable export control laws and regulations promulgated and administered by
* the governments of any countries asserting jurisdiction over the parties or transactions.
**/

/**
*\*\file      spi_flash.c
*\*\author    Nations
*\*\version   v1.0.0
*\*\copyright Copyright (c) 2023, Nations Technologies Inc. All rights reserved.
*/
#include "spi_flash.h"



/** SPI_FLASH_Private_Functions **/

SPI_InitType SPI_InitStructure;

/**
*\*\name    sFLASH_LowLevel_DeInit.
*\*\fun     DeInitializes the peripherals used by the SPI FLASH driver.
*\*\param   none
*\*\return  none
**/
void sFLASH_LowLevel_DeInit(void)
{
    GPIO_InitType GPIO_InitStructure;

    GPIO_InitStruct(&GPIO_InitStructure);
    /*!< Disable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, DISABLE);

    /*!< DeInitializes the sFLASH_SPI */
    SPI_I2S_DeInit(sFLASH_SPI);

    /*!< sFLASH_SPI Periph clock disable */
    RCC_EnableAPB2PeriphClk(sFLASH_SPI_CLK, DISABLE);

    /*!< Configure sFLASH_SPI pins: SCK */
    GPIO_InitStructure.Pin       = sFLASH_SPI_SCK_PIN;
    GPIO_InitStructure.GPIO_Pull = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = sFLASH_SPI_SCK_GPIO_AF;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_INPUT;
    GPIO_InitPeripheral(sFLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);

    /*!< Configure sFLASH_SPI pins: MISO */
    GPIO_InitStructure.Pin = sFLASH_SPI_MISO_PIN;
    GPIO_InitStructure.GPIO_Pull = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = sFLASH_SPI_MISO_GPIO_AF;
    GPIO_InitPeripheral(sFLASH_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);

    /*!< Configure sFLASH_SPI pins: MOSI */
    GPIO_InitStructure.Pin = sFLASH_SPI_MOSI_PIN;
    GPIO_InitStructure.GPIO_Pull = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = sFLASH_SPI_MOSI_GPIO_AF;
    GPIO_InitPeripheral(sFLASH_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);

    /*!< Configure sFLASH_CS_PIN pin: sFLASH Card CS pin */
    GPIO_InitStructure.Pin = sFLASH_CS_PIN;
    GPIO_InitStructure.GPIO_Pull = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = sFLASH_CS_GPIO_AF;
    GPIO_InitPeripheral(sFLASH_CS_GPIO_PORT, &GPIO_InitStructure);
}

/**
*\*\name    sFLASH_LowLevel_Init.
*\*\fun     Initializes the peripherals used by the SPI FLASH driver.
*\*\param   none
*\*\return  none
**/
void sFLASH_LowLevel_Init(void)
{
    GPIO_InitType GPIO_InitStructure;

    GPIO_InitStruct(&GPIO_InitStructure);

    /*!< sFLASH_SPI_CS_GPIO, sFLASH_SPI_MOSI_GPIO, sFLASH_SPI_MISO_GPIO
         and sFLASH_SPI_SCK_GPIO Periph clock enable */
    RCC_EnableAHB1PeriphClk(
        sFLASH_CS_GPIO_CLK | sFLASH_SPI_MOSI_GPIO_CLK | sFLASH_SPI_MISO_GPIO_CLK | sFLASH_SPI_SCK_GPIO_CLK, ENABLE);

    /*!< sFLASH_SPI Periph clock enable */
    RCC_EnableAPB2PeriphClk(sFLASH_SPI_CLK, ENABLE);

    /*!< Configure sFLASH_SPI pins: SCK */
    GPIO_InitStructure.Pin        = sFLASH_SPI_SCK_PIN;
    GPIO_InitStructure.GPIO_Pull  = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = sFLASH_SPI_SCK_GPIO_AF;
    GPIO_InitStructure.GPIO_Mode  = GPIO_MODE_AF_PP;
    GPIO_InitPeripheral(sFLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);

    /*!< Configure sFLASH_SPI pins: MOSI */
    GPIO_InitStructure.Pin = sFLASH_SPI_MOSI_PIN;
    GPIO_InitStructure.GPIO_Pull = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = sFLASH_SPI_MOSI_GPIO_AF;
    GPIO_InitPeripheral(sFLASH_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);

    /*!< Configure sFLASH_SPI pins: MISO */
    GPIO_InitStructure.Pin       = sFLASH_SPI_MISO_PIN;
    GPIO_InitStructure.GPIO_Pull = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = sFLASH_SPI_MISO_GPIO_AF;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_INPUT;
    GPIO_InitPeripheral(sFLASH_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);

    /*!< Configure sFLASH_CS_PIN pin: sFLASH Card CS pin */
    GPIO_InitStructure.Pin       = sFLASH_CS_PIN;
    GPIO_InitStructure.GPIO_Pull = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = sFLASH_CS_GPIO_AF;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitPeripheral(sFLASH_CS_GPIO_PORT, &GPIO_InitStructure);
}

/**
*\*\name    sFLASH_DeInit.
*\*\fun     DeInitializes the peripherals used by the SPI FLASH driver.
*\*\param   none
*\*\return  none
**/
void sFLASH_DeInit(void)
{
    sFLASH_LowLevel_DeInit();
}

/**
*\*\name    sFLASH_Init.
*\*\fun     Initializes the peripherals used by the SPI FLASH driver.
*\*\param   none
*\*\return  none
**/
void sFLASH_Init(void)
{
    sFLASH_LowLevel_Init();

    /*!< Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();

    /*!< SPI configuration */
    SPI_InitStruct(&SPI_InitStructure);
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
    SPI_InitStructure.SpiMode       = SPI_MODE_MASTER;
    SPI_InitStructure.DataLen       = SPI_DATA_SIZE_8BITS;
    SPI_InitStructure.CLKPOL        = SPI_CLKPOL_HIGH;
    SPI_InitStructure.CLKPHA        = SPI_CLKPHA_SECOND_EDGE;
    SPI_InitStructure.NSS           = SPI_NSS_SOFT;

    SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_8;

    SPI_InitStructure.FirstBit = SPI_FB_MSB;
    SPI_InitStructure.CRCPoly  = 7;
    SPI_Init(sFLASH_SPI, &SPI_InitStructure);

    /*!< Enable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, ENABLE);
}


/**
*\*\name    DMA_Flash_Init.
*\*\fun     FLASH DMA INIT.
*\*\param   none
*\*\return  none
**/
void sFLASH_DMA_Init(uint8_t TransmitType, uint32_t MemoryAddr, uint32_t BufferLen)
{
    DMA_InitType DMA_InitStructure;

    /*ENABLE DMA CLK*/
    RCC_EnableAHBPeriphClk(sFLASH_SPI_DMA_CLK, ENABLE);

    if(TransmitType == DMA_RECV)
    {
        /* SPI_FLASH_Rx_DMA_Channel configuration ---------------------------------------------*/
        DMA_DeInit(sFLASH_SPI_Rx_DMA_Channel);
        DMA_StructInit(&DMA_InitStructure);
        DMA_InitStructure.PeriphAddr     = (uint32_t)&sFLASH_SPI->DAT;
        DMA_InitStructure.MemAddr        = (uint32_t)MemoryAddr;
        DMA_InitStructure.Direction      = DMA_DIR_PERIPH_SRC;
        DMA_InitStructure.BufSize        = BufferLen;
        DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;
        DMA_InitStructure.MemoryInc 	 = DMA_MEM_INC_ENABLE;
        DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE;
        DMA_InitStructure.MemDataSize    = DMA_MEM_DATA_WIDTH_BYTE;
        DMA_InitStructure.CircularMode   = DMA_MODE_NORMAL;
        DMA_InitStructure.Priority       = DMA_PRIORITY_VERY_HIGH;
        DMA_InitStructure.Mem2Mem        = DMA_M2M_DISABLE;
        DMA_Init(sFLASH_SPI_Rx_DMA_Channel, &DMA_InitStructure);
        DMA_RequestRemap(sFLASH_SPI_Rx_DMA_Remap, sFLASH_SPI_Rx_DMA_Channel, ENABLE);
    }
    else
    {
        /* SPI_FLASH_Tx_DMA_Channel configuration ---------------------------------------------*/
        DMA_DeInit(sFLASH_SPI_Tx_DMA_Channel);
        DMA_StructInit(&DMA_InitStructure);
        DMA_InitStructure.PeriphAddr     = (uint32_t)&sFLASH_SPI->DAT;
        DMA_InitStructure.MemAddr        = (uint32_t)MemoryAddr;
        DMA_InitStructure.Direction      = DMA_DIR_PERIPH_DST;
        DMA_InitStructure.BufSize        = BufferLen;
        DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;
        DMA_InitStructure.MemoryInc 	 = DMA_MEM_INC_ENABLE;
        DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE;
        DMA_InitStructure.MemDataSize    = DMA_MEM_DATA_WIDTH_BYTE;
        DMA_InitStructure.CircularMode   = DMA_MODE_NORMAL;
        DMA_InitStructure.Priority       = DMA_PRIORITY_VERY_HIGH;
        DMA_InitStructure.Mem2Mem        = DMA_M2M_DISABLE;
        DMA_Init(sFLASH_SPI_Tx_DMA_Channel, &DMA_InitStructure);
        DMA_RequestRemap(sFLASH_SPI_Tx_DMA_Remap, sFLASH_SPI_Tx_DMA_Channel, ENABLE);
    }
}

/**
*\*\name    sFLASH_EraseSector.
*\*\fun     Erases the specified FLASH sector.
*\*\param   SectorAddr address of the sector to erase.
*\*\return  none
**/
void sFLASH_EraseSector(uint32_t SectorAddr)
{
    /*!< Send write enable instruction */
    sFLASH_WriteEnable();

    /*!< Sector Erase */
    /*!< Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();
    /*!< Send Sector Erase instruction */
    sFLASH_SendByte(sFLASH_CMD_SE);
    /*!< Send SectorAddr high nibble address byte */
    sFLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
    /*!< Send SectorAddr medium nibble address byte */
    sFLASH_SendByte((SectorAddr & 0xFF00) >> 8);
    /*!< Send SectorAddr low nibble address byte */
    sFLASH_SendByte(SectorAddr & 0xFF);
    /*!< Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();

    /*!< Wait the end of Flash writing */
    sFLASH_WaitForWriteEnd();
}

/**
*\*\name    sFLASH_EraseBulk.
*\*\fun     Erases the entire FLASH.
*\*\param   none
*\*\return  none
**/
void sFLASH_EraseBulk(void)
{
    /*!< Send write enable instruction */
    sFLASH_WriteEnable();

    /*!< Bulk Erase */
    /*!< Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();
    /*!< Send Bulk Erase instruction  */
    sFLASH_SendByte(sFLASH_CMD_BE);
    /*!< Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();

    /*!< Wait the end of Flash writing */
    sFLASH_WaitForWriteEnd();
}

/**
*\*\name    sFLASH_WritePage.
*\*\fun     Writes more than one byte to the FLASH with a single WRITE cycle (Page WRITE sequence).
*\*\param   pBuffer pointer to the buffer  containing the data to be written to the FLASH.
*\*\param   WriteAddr FLASH's internal address to write to.
*\*\param   NumByteToWrite number of bytes to write to the FLASH, must be equal or less than "sFLASH_PAGESIZE" value.
*\*\return  none
*\*\note    The number of byte can't exceed the FLASH page size.
**/
void sFLASH_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
    /*!< Enable the write access to the FLASH */
    sFLASH_WriteEnable();

    /*!< Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();
    /*!< Send "Write to Memory " instruction */
    sFLASH_SendByte(sFLASH_CMD_WRITE);
    /*!< Send WriteAddr high nibble address byte to write to */
    sFLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
    /*!< Send WriteAddr medium nibble address byte to write to */
    sFLASH_SendByte((WriteAddr & 0xFF00) >> 8);
    /*!< Send WriteAddr low nibble address byte to write to */
    sFLASH_SendByte(WriteAddr & 0xFF);

    /*!< while there is data to be written on the FLASH */
    while (NumByteToWrite--)
    {
        /*!< Send the current byte */
        sFLASH_SendByte(*pBuffer);
        /*!< Point on the next byte to be written */
        pBuffer++;
    }

    /*!< Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();

    /*!< Wait the end of Flash writing */
    sFLASH_WaitForWriteEnd();
}

/**
*\*\name    sFLASH_WriteBuffer.
*\*\fun     Writes block of data to the FLASH. In this function, the number of WRITE cycles are reduced, using Page WRITE sequence.
*\*\param   pBuffer pointer to the buffer  containing the data to be written to the FLASH.
*\*\param   WriteAddr FLASH's internal address to write to.
*\*\param   NumByteToWrite number of bytes to write to the FLASH.
*\*\return  none
**/
void sFLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
    uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

    Addr        = WriteAddr % sFLASH_SPI_PAGESIZE;
    count       = sFLASH_SPI_PAGESIZE - Addr;
    NumOfPage   = NumByteToWrite / sFLASH_SPI_PAGESIZE;
    NumOfSingle = NumByteToWrite % sFLASH_SPI_PAGESIZE;

    if (Addr == 0) /*!< WriteAddr is sFLASH_PAGESIZE aligned  */
    {
        if (NumOfPage == 0) /*!< NumByteToWrite < sFLASH_PAGESIZE */
        {
            sFLASH_WritePage(pBuffer, WriteAddr, NumByteToWrite);
        }
        else /*!< NumByteToWrite > sFLASH_PAGESIZE */
        {
            while (NumOfPage--)
            {
                sFLASH_WritePage(pBuffer, WriteAddr, sFLASH_SPI_PAGESIZE);
                WriteAddr += sFLASH_SPI_PAGESIZE;
                pBuffer += sFLASH_SPI_PAGESIZE;
            }

            sFLASH_WritePage(pBuffer, WriteAddr, NumOfSingle);
        }
    }
    else /*!< WriteAddr is not sFLASH_PAGESIZE aligned  */
    {
        if (NumOfPage == 0) /*!< NumByteToWrite < sFLASH_PAGESIZE */
        {
            if (NumOfSingle > count) /*!< (NumByteToWrite + WriteAddr) > sFLASH_PAGESIZE */
            {
                temp = NumOfSingle - count;

                sFLASH_WritePage(pBuffer, WriteAddr, count);
                WriteAddr += count;
                pBuffer += count;

                sFLASH_WritePage(pBuffer, WriteAddr, temp);
            }
            else
            {
                sFLASH_WritePage(pBuffer, WriteAddr, NumByteToWrite);
            }
        }
        else /*!< NumByteToWrite > sFLASH_PAGESIZE */
        {
            NumByteToWrite -= count;
            NumOfPage   = NumByteToWrite / sFLASH_SPI_PAGESIZE;
            NumOfSingle = NumByteToWrite % sFLASH_SPI_PAGESIZE;

            sFLASH_WritePage(pBuffer, WriteAddr, count);
            WriteAddr += count;
            pBuffer += count;

            while (NumOfPage--)
            {
                sFLASH_WritePage(pBuffer, WriteAddr, sFLASH_SPI_PAGESIZE);
                WriteAddr += sFLASH_SPI_PAGESIZE;
                pBuffer += sFLASH_SPI_PAGESIZE;
            }

            if (NumOfSingle != 0)
            {
                sFLASH_WritePage(pBuffer, WriteAddr, NumOfSingle);
            }
        }
    }
}

/**
*\*\name    sFLASH_ReadBuffer.
*\*\fun     Reads a block of data from the FLASH.
*\*\param   pBuffer pointer to the buffer that receives the data read from the FLASH.
*\*\param   ReadAddr FLASH's internal address to read from.
*\*\param   NumByteToRead number of bytes to read from the FLASH.
*\*\return  none
**/
void sFLASH_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
    /*!< Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();

    /*!< Send "Read from Memory " instruction */
    sFLASH_SendByte(sFLASH_CMD_READ);

    /*!< Send ReadAddr high nibble address byte to read from */
    sFLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    /*!< Send ReadAddr medium nibble address byte to read from */
    sFLASH_SendByte((ReadAddr & 0xFF00) >> 8);
    /*!< Send ReadAddr low nibble address byte to read from */
    sFLASH_SendByte(ReadAddr & 0xFF);

    while (NumByteToRead--) /*!< while there is data to be read */
    {
        /*!< Read a byte from the FLASH */
        *pBuffer = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
        /*!< Point to the next location where the byte read will be saved */
        pBuffer++;
    }

    /*!< Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();
}

/**
*\*\name    sFLASH_ReadID.
*\*\fun     Reads FLASH identification.
*\*\param   none
*\*\return  FLASH identification
**/
uint32_t sFLASH_ReadID(void)
{
    uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

    /*!< Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();

    /*!< Send "RDID " instruction */
    sFLASH_SendByte(0x9F);
    /*!< Read a byte from the FLASH */
    Temp0 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);

    /*!< Read a byte from the FLASH */
    Temp1 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);

    /*!< Read a byte from the FLASH */
    Temp2 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);

    /*!< Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();

    Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;

    return Temp;
}

/**
*\*\name    sFLASH_StartReadSequence.
*\*\fun     Initiates a read data byte (READ) sequence from the Flash.
*\*\param   ReadAddr FLASH's internal address to read from.
*\*\return  none
*\*\note    This is done by driving the /CS line low to select the device, then the READ
*\*\				instruction is transmitted followed by 3 bytes address. This function exit
*\*\				and keep the /CS line low, so the Flash still being selected. With this
*\*\				technique the whole content of the Flash is read with a single READ instruction.
**/
void sFLASH_StartReadSequence(uint32_t ReadAddr)
{
    /*!< Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();

    /*!< Send "Read from Memory " instruction */
    sFLASH_SendByte(sFLASH_CMD_READ);

    /*!< Send the 24-bit address of the address to read from -------------------*/
    /*!< Send ReadAddr high nibble address byte */
    sFLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    /*!< Send ReadAddr medium nibble address byte */
    sFLASH_SendByte((ReadAddr & 0xFF00) >> 8);
    /*!< Send ReadAddr low nibble address byte */
    sFLASH_SendByte(ReadAddr & 0xFF);
}

/**
*\*\name    sFLASH_ReadByte.
*\*\fun     Reads a byte from the SPI Flash.
*\*\param   none
*\*\return  Byte Read from the SPI Flash.
*\*\note    This function must be used only if the Start_Read_Sequence function has been previously called.
**/
uint8_t sFLASH_ReadByte(void)
{
    return (sFLASH_SendByte(sFLASH_DUMMY_BYTE));
}

/**
*\*\name    sFLASH_SendByte.
*\*\fun     Sends a byte through the SPI interface and return the byte received from the SPI bus.
*\*\param   byte byte to send.
*\*\return  The value of the received byte.
**/
uint8_t sFLASH_SendByte(uint8_t byte)
{
    /*!< Loop while DAT register in not emplty */
    while (SPI_I2S_GetStatus(sFLASH_SPI, SPI_I2S_TE_FLAG) == RESET)
        ;

    /*!< Send byte through the SPI1 peripheral */
    SPI_I2S_TransmitData(sFLASH_SPI, byte);

    /*!< Wait to receive a byte */
    while (SPI_I2S_GetStatus(sFLASH_SPI, SPI_I2S_RNE_FLAG) == RESET)
        ;

    /*!< Return the byte read from the SPI bus */
    return SPI_I2S_ReceiveData(sFLASH_SPI);
}

/**
*\*\name    sFLASH_SendHalfWord.
*\*\fun     Sends a Half Word through the SPI interface and return the Half Word received from the SPI bus.
*\*\param   HalfWord Half Word to send.
*\*\return  The value of the received Half Word.
**/
uint16_t sFLASH_SendHalfWord(uint16_t HalfWord)
{
    /*!< Loop while DAT register in not emplty */
    while (SPI_I2S_GetStatus(sFLASH_SPI, SPI_I2S_TE_FLAG) == RESET)
        ;

    /*!< Send Half Word through the sFLASH peripheral */
    SPI_I2S_TransmitData(sFLASH_SPI, HalfWord);

    /*!< Wait to receive a Half Word */
    while (SPI_I2S_GetStatus(sFLASH_SPI, SPI_I2S_RNE_FLAG) == RESET)
        ;

    /*!< Return the Half Word read from the SPI bus */
    return SPI_I2S_ReceiveData(sFLASH_SPI);
}

/**
*\*\name    sFLASH_WriteEnable.
*\*\fun     Enables the write access to the FLASH.
*\*\param   none
*\*\return  none
**/
void sFLASH_WriteEnable(void)
{
    /*!< Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();

    /*!< Send "Write Enable" instruction */
    sFLASH_SendByte(sFLASH_CMD_WREN);

    /*!< Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();
}

/**
*\*\name    sFLASH_WaitForWriteEnd.
*\*\fun     Polls the status of the Write In Progress (WIP) flag in the FLASH's status register and loop until write opertaion has completed.
*\*\param   none
*\*\return  none
**/
void sFLASH_WaitForWriteEnd(void)
{
    uint8_t flashstatus = 0;

    /*!< Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();

    /*!< Send "Read Status Register" instruction */
    sFLASH_SendByte(sFLASH_CMD_RDSR);

    /*!< Loop as long as the memory is busy with a write cycle */
    do
    {
        /*!< Send a dummy byte to generate the clock needed by the FLASH
        and put the value of the status register in FLASH_STS variable */
        flashstatus = sFLASH_SendByte(sFLASH_DUMMY_BYTE);

    }
    while ((flashstatus & sFLASH_WIP_FLAG) == SET);   /* Write in progress */

    /*!< Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();
}

/**
*\*\name    SPI_DMA_PageWrite.
*\*\fun     Writes block of data to the FLASH by DMA.
*\*\param   WriteAddr FLASH's internal address to write to.
*\*\return  none
**/
void SPI_DMA_PageWrite(uint32_t WriteAddr)
{
    /* Enable the write access to the FLASH */
    sFLASH_WriteEnable();

    /* Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();
    /* Send "Write to Memory " instruction */
    sFLASH_SendByte(sFLASH_CMD_WRITE);
    /*!< Send WriteAddr high nibble address byte to write to */
    sFLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
    /*!< Send WriteAddr medium nibble address byte to write to */
    sFLASH_SendByte((WriteAddr & 0xFF00) >> 8);
    /*!< Send WriteAddr low nibble address byte to write to */
    sFLASH_SendByte(WriteAddr & 0xFF);

    /* Enable DMA1 Channel3 */
    DMA_EnableChannel(sFLASH_SPI_Tx_DMA_Channel, ENABLE);
    /* Enable sFLASH_SPI Tx request */
    SPI_I2S_EnableDma(sFLASH_SPI, SPI_I2S_DMA_TX, ENABLE);

    /* Wait for DMA1 channel3 transfer complete */
    while(!DMA_GetFlagStatus(sFLASH_SPI_Tx_DMA_FLAG, sFLASH_SPI_DMA));

    /* Wait for sFLASH_SPI transfer complete */
    while(SPI_I2S_GetStatus(sFLASH_SPI, SPI_I2S_BUSY_FLAG));

    /* Disnable sFLASH_SPI Tx request */
    SPI_I2S_EnableDma(sFLASH_SPI, SPI_I2S_DMA_TX, DISABLE);
    /* Disable DMA1 Channel3 */
    DMA_EnableChannel(sFLASH_SPI_Tx_DMA_Channel, DISABLE);

    /* Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();

    /*!< Disable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, DISABLE);
    /*!< DeInitializes the sFLASH_SPI */
    SPI_I2S_DeInit(sFLASH_SPI);

    /*!< Set the sFLASH_SPI Doubleline FullDuplex*/
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
    SPI_Init(sFLASH_SPI, &SPI_InitStructure);

    /*!< Enable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, ENABLE);

    /*clear TX buff*/
    sFLASH_SPI->DAT;

    /*!< Wait the end of Flash writing */
    sFLASH_WaitForWriteEnd();
}

/**
*\*\name    SPI_DMA_BufferRead.
*\*\fun     Reads a block of data from the FLASH by DMA.
*\*\param   ReadAddr FLASH's internal address to read from.
*\*\return  none
**/
void SPI_DMA_BufferRead(uint32_t ReadAddr)
{
    /* Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();

    /* Send "Read from Memory " instruction */
    sFLASH_SendByte(sFLASH_CMD_READ);

    /* Send ReadAddr high nibble address byte to read from */
    sFLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    /* Send ReadAddr medium nibble address byte to read from */
    sFLASH_SendByte((ReadAddr & 0xFF00) >> 8);
    /* Send ReadAddr low nibble address byte to read from */
    sFLASH_SendByte(ReadAddr & 0xFF);

    /*!< Disable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, DISABLE);
    /*!< DeInitializes the sFLASH_SPI */
    SPI_I2S_DeInit(sFLASH_SPI);

    /*!< Set the sFLASH_SPI ReadOnly*/
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_RONLY;
    SPI_Init(sFLASH_SPI, &SPI_InitStructure);

    /* Enable DMA1 Channel2 */
    DMA_EnableChannel(sFLASH_SPI_Rx_DMA_Channel, ENABLE);
    /* Enable sFLASH_SPI Rx request */
    SPI_I2S_EnableDma(sFLASH_SPI, SPI_I2S_DMA_RX, ENABLE);

    /*!< Enable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, ENABLE);

    /* Wait for sFLASH_SPI_Rx_DMA_FLAG transfer complete */
    while (!DMA_GetFlagStatus(sFLASH_SPI_Rx_DMA_FLAG, sFLASH_SPI_DMA));

    /* Disnable sFLASH_SPI Rx request */
    SPI_I2S_EnableDma(sFLASH_SPI, SPI_I2S_DMA_RX, DISABLE);
    /* Disable DMA1 Channel2 */
    DMA_EnableChannel(sFLASH_SPI_Rx_DMA_Channel, DISABLE);

    /* Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();

    /*!< Disable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, DISABLE);
    /*!< DeInitializes the sFLASH_SPI */
    SPI_I2S_DeInit(sFLASH_SPI);

    systick_delay_us(100);

    /*!< Set the sFLASH_SPI Doubleline FullDuplex*/
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
    SPI_Init(sFLASH_SPI, &SPI_InitStructure);

    /*!< Enable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, ENABLE);

    /*clear RX buff*/
    sFLASH_SPI->DAT;

    sFLASH_WaitForWriteEnd();
}

/**
*\*\name    sFLASH_DMA_PageWrite.
*\*\fun     Writes block of data to the FLASH by DMA.
*\*\param   WriteAddr FLASH's internal address to write to.
*\*\return  none
**/
void sFLASH_DMA_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
    /* Enable the write access to the FLASH */
    sFLASH_WriteEnable();

    /* Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();
    /* Send "Write to Memory " instruction */
    sFLASH_SendByte(sFLASH_CMD_WRITE);
    /*!< Send WriteAddr high nibble address byte to write to */
    sFLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
    /*!< Send WriteAddr medium nibble address byte to write to */
    sFLASH_SendByte((WriteAddr & 0xFF00) >> 8);
    /*!< Send WriteAddr low nibble address byte to write to */
    sFLASH_SendByte(WriteAddr & 0xFF);


    /*!< Disable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, DISABLE);
    /*!< DeInitializes the sFLASH_SPI */
    SPI_I2S_DeInit(sFLASH_SPI);
    /*!< Set the sFLASH_SPI SendOnly*/
    SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX;
    SPI_Init(sFLASH_SPI, &SPI_InitStructure);

    sFLASH_DMA_Init(DMA_SEND, (uint32_t)pBuffer, NumByteToWrite);
    /* Enable DMA1 Channel3 */
    DMA_EnableChannel(sFLASH_SPI_Tx_DMA_Channel, ENABLE);
    /* Enable sFLASH_SPI Tx request */
    SPI_I2S_EnableDma(sFLASH_SPI, SPI_I2S_DMA_TX, ENABLE);
    /*!< Enable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, ENABLE);

    /* Wait for DMA1 channel3 transfer complete */
    while(!DMA_GetFlagStatus(sFLASH_SPI_Tx_DMA_FLAG, sFLASH_SPI_DMA));

    /* Wait for sFLASH_SPI transfer complete */
    while(SPI_I2S_GetStatus(sFLASH_SPI, SPI_I2S_BUSY_FLAG));

    /* Disnable sFLASH_SPI Tx request */
    SPI_I2S_EnableDma(sFLASH_SPI, SPI_I2S_DMA_TX, DISABLE);
    /* Disable DMA1 Channel3 */
    DMA_EnableChannel(sFLASH_SPI_Tx_DMA_Channel, DISABLE);

    /* Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();

    /*!< Disable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, DISABLE);
    /*!< DeInitializes the sFLASH_SPI */
    SPI_I2S_DeInit(sFLASH_SPI);

    /*!< Set the sFLASH_SPI Doubleline FullDuplex*/
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
    SPI_Init(sFLASH_SPI, &SPI_InitStructure);

    /*!< Enable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, ENABLE);

    /*clear TX buff*/
    sFLASH_SPI->DAT;

    /*!< Wait the end of Flash writing */
    sFLASH_WaitForWriteEnd();
}

/**
*\*\name    sFLASH_DMA_WriteBuffer.
*\*\fun     Writes block of data to the FLASH. In this function, the number of WRITE cycles are reduced, using Page WRITE sequence.
*\*\param   pBuffer pointer to the buffer  containing the data to be written to the FLASH.
*\*\param   WriteAddr FLASH's internal address to write to.
*\*\param   NumByteToWrite number of bytes to write to the FLASH.
*\*\return  none
**/
void sFLASH_DMA_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
    uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

    Addr        = WriteAddr % sFLASH_SPI_PAGESIZE;
    count       = sFLASH_SPI_PAGESIZE - Addr;
    NumOfPage   = NumByteToWrite / sFLASH_SPI_PAGESIZE;
    NumOfSingle = NumByteToWrite % sFLASH_SPI_PAGESIZE;

    if (Addr == 0) /*!< WriteAddr is sFLASH_PAGESIZE aligned  */
    {
        if (NumOfPage == 0) /*!< NumByteToWrite < sFLASH_PAGESIZE */
        {
            sFLASH_DMA_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
        }
        else /*!< NumByteToWrite > sFLASH_PAGESIZE */
        {
            while (NumOfPage--)
            {
                sFLASH_DMA_PageWrite(pBuffer, WriteAddr, sFLASH_SPI_PAGESIZE);
                WriteAddr += sFLASH_SPI_PAGESIZE;
                pBuffer += sFLASH_SPI_PAGESIZE;
            }

            sFLASH_DMA_PageWrite(pBuffer, WriteAddr, NumOfSingle);
        }
    }
    else /*!< WriteAddr is not sFLASH_PAGESIZE aligned  */
    {
        if (NumOfPage == 0) /*!< NumByteToWrite < sFLASH_PAGESIZE */
        {
            if (NumOfSingle > count) /*!< (NumByteToWrite + WriteAddr) > sFLASH_PAGESIZE */
            {
                temp = NumOfSingle - count;

                sFLASH_DMA_PageWrite(pBuffer, WriteAddr, count);
                WriteAddr += count;
                pBuffer += count;

                sFLASH_DMA_PageWrite(pBuffer, WriteAddr, temp);
            }
            else
            {
                sFLASH_DMA_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
            }
        }
        else /*!< NumByteToWrite > sFLASH_PAGESIZE */
        {
            NumByteToWrite -= count;
            NumOfPage   = NumByteToWrite / sFLASH_SPI_PAGESIZE;
            NumOfSingle = NumByteToWrite % sFLASH_SPI_PAGESIZE;

            sFLASH_DMA_PageWrite(pBuffer, WriteAddr, count);
            WriteAddr += count;
            pBuffer += count;

            while (NumOfPage--)
            {
                sFLASH_DMA_PageWrite(pBuffer, WriteAddr, sFLASH_SPI_PAGESIZE);
                WriteAddr += sFLASH_SPI_PAGESIZE;
                pBuffer += sFLASH_SPI_PAGESIZE;
            }

            if (NumOfSingle != 0)
            {
                sFLASH_DMA_PageWrite(pBuffer, WriteAddr, NumOfSingle);
            }
        }
    }
}

/**
*\*\name    sFLASH_DMA_ReadBuffer.
*\*\fun     Reads a block of data from the FLASH by DMA.
*\*\param   ReadAddr FLASH's internal address to read from.
*\*\return  none
**/
void sFLASH_DMA_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
    /* Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();

    /* Send "Read from Memory " instruction */
    sFLASH_SendByte(sFLASH_CMD_READ);

    /* Send ReadAddr high nibble address byte to read from */
    sFLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    /* Send ReadAddr medium nibble address byte to read from */
    sFLASH_SendByte((ReadAddr & 0xFF00) >> 8);
    /* Send ReadAddr low nibble address byte to read from */
    sFLASH_SendByte(ReadAddr & 0xFF);

    /*!< Disable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, DISABLE);
    /*!< DeInitializes the sFLASH_SPI */
    SPI_I2S_DeInit(sFLASH_SPI);

    /*!< Set the sFLASH_SPI ReadOnly*/
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_RONLY;
    SPI_Init(sFLASH_SPI, &SPI_InitStructure);

    sFLASH_DMA_Init(DMA_RECV, (uint32_t)pBuffer, NumByteToRead);
    /* Enable DMA1 Channel2 */
    DMA_EnableChannel(sFLASH_SPI_Rx_DMA_Channel, ENABLE);
    /* Enable sFLASH_SPI Rx request */
    SPI_I2S_EnableDma(sFLASH_SPI, SPI_I2S_DMA_RX, ENABLE);

    /*!< Enable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, ENABLE);

    /* Wait for sFLASH_SPI_Rx_DMA_FLAG transfer complete */
    while (!DMA_GetFlagStatus(sFLASH_SPI_Rx_DMA_FLAG, sFLASH_SPI_DMA));

    /* Disnable sFLASH_SPI Rx request */
    SPI_I2S_EnableDma(sFLASH_SPI, SPI_I2S_DMA_RX, DISABLE);
    /* Disable DMA1 Channel2 */
    DMA_EnableChannel(sFLASH_SPI_Rx_DMA_Channel, DISABLE);

    /* Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();

    /*!< Disable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, DISABLE);
    /*!< DeInitializes the sFLASH_SPI */
    SPI_I2S_DeInit(sFLASH_SPI);

    systick_delay_us(100);

    /*!< Set the sFLASH_SPI Doubleline FullDuplex*/
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
    SPI_Init(sFLASH_SPI, &SPI_InitStructure);

    /*!< Enable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, ENABLE);

    /*clear RX buff*/
    sFLASH_SPI->DAT;

    sFLASH_WaitForWriteEnd();
}

