This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

FATFS+QSPI+USBmsc bug

hello,nordic

    I use NRF52840 QSPI+FatFs+USB-msc(..\nRF5_SDK_15.2.0_9412b96\nRF5_SDK_15.2.0_9412b96\examples\peripheral\usbd_msc),I found there is a bug about write to flash and read from falsh.

bug description: 

nrfx_err_t nrfx_qspi_write(void const * p_tx_buffer,size_t tx_buffer_length,uint32_t dst_address);

nrfx_err_t nrfx_qspi_read(void * p_rx_buffer,size_t rx_buffer_length,uint32_t src_address);

If the p_tx_buffer and p_rx_buffer addresses are not four-byte aligned, the read data will be different from the previously written data.

In  NRF52840 QSPI+FatFs+USB-msc this example didn't process the address alingment .I test the example like this


uint8_t a[9] = {0,1,2,4,8};
uint8_t file_buf[599] = {1,2,3,4,5,6,7,8,9,10};
static void fatfs_file_create(void)
{
FRESULT ff_result;
FIL file;
char filename[16];
uint32_t bww;

if (m_usb_connected)
{
NRF_LOG_ERROR("Unable to operate on filesystem while USB is connected");
return;
}

(void)snprintf(filename, sizeof(filename), "%08x.txt", rand());

NRF_LOG_RAW_INFO("Creating random file: %s ...file_buf addr:%x,a :%x", (uint32_t)filename, file_buf, a);
NRF_LOG_FLUSH();

ff_result = f_open(&file, filename, FA_WRITE | FA_CREATE_ALWAYS);
if (ff_result != FR_OK)
{
NRF_LOG_ERROR("\r\nUnable to open or create file: %u", ff_result);
NRF_LOG_FLUSH();
return;
}

f_write(&file, file_buf, 599, &bww);
f_close(&file);

f_open(&file, filename, FA_READ);
f_read(&file, file_buf, 599, &bww);

NRF_LOG_INFO("%d, %d, %d, %d\r\n", file_buf[0], file_buf[1], file_buf[2], file_buf[3]);

ff_result = f_close(&file);
if (ff_result != FR_OK)
{
NRF_LOG_ERROR("\r\nUnable to close file: %u", ff_result);
NRF_LOG_FLUSH();
return;
}
NRF_LOG_RAW_INFO("done\r\n");
}

log info:

Creating random file: 0000181c.txt ...file_buf addr:20000015,a :2000000C

[00:00:00.000,000] <info> app: 2, 3, 4, 5

  • I think it is the same problem, just modify the QSPI block driver code<nrf_block_dev_qspi.c>, let the write/read buf pointer address aligned 4bytes.

  • Your response got deleted biaccidentally.

    "Hi,Jeff

    Not modified here, the p_erase_unit_buff inside the structure is already aligned with 4 bytes.

    static ret_code_t block_dev_qspi_read_req(nrf_block_dev_t const * p_blk_dev,
    nrf_block_req_t const * p_blk)

    {

    ...

    p_work->left_req = *p_blk;
    p_work->req = *p_blk;
    nrf_block_req_t * p_blk_left = &p_work->left_req;

    p_work->state = NRF_BLOCK_DEV_QSPI_STATE_READ_EXEC;

    ret = nrf_drv_qspi_read(p_blk_left->p_buff,
    p_blk_left->blk_count * p_work->geometry.blk_size,
    p_blk_left->blk_id * p_work->geometry.blk_size);

    ...

    }

    p_blk_left->p_buff , this buf's address maybe not aligned 4-bytes.

    my project use freeRTOS, I don't use the example<nrf_block_dev_qspi.c>,I changed the code. Attach my code,you can refer to this."
     

    /**
     * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    #include "QSPI_driver.h" 
    #include "my_block_dev_qspi.h"
    /**@file
     *
     * @ingroup nrf_block_dev_qspi
     * @{
     *
     * @brief This module implements block device API. It should be used as a reference block device.
     */
    #define MY_BD_PAGE_PROGRAM_SIZE 256    /**< Page program size (minimum block size)*/
    
    /**
     * @brief Block to erase unit translation
     *
     * @param blk_id    Block index
     * @param blk_size  Block size
     * */
    #define MY_BD_BLOCK_TO_ERASEUNIT(blk_id, blk_size)   \
        ((blk_id) * (blk_size)) / (MY_BLOCK_DEV_QSPI_ERASE_UNIT_SIZE)
    
    /**
     * @brief Blocks per erase unit
     *
     * @param blk_size  Block size
     * */
    #define MY_BD_BLOCKS_PER_ERASEUNIT(blk_size)         \
        (MY_BLOCK_DEV_QSPI_ERASE_UNIT_SIZE / (blk_size))
    
    
    /**
     * @brief Active QSPI block device handle. Only one instance.
     * */
    
    /*************************************************************************   
    **	function name:	my_block_dev_qspi_init 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    static ret_code_t my_block_dev_qspi_init(nrf_block_dev_t const *  p_blk_dev,
                                             nrf_block_dev_ev_handler ev_handler,
                                             void const * p_context)
    {
        ASSERT(p_blk_dev);
        my_block_dev_qspi_t const *  p_qspi_dev =
                                      CONTAINER_OF(p_blk_dev, my_block_dev_qspi_t, block_dev);
        my_block_dev_qspi_work_t *   p_work = p_qspi_dev->p_work;
    
    
        
        if (p_qspi_dev->qspi_bdev_config.block_size % MY_BD_PAGE_PROGRAM_SIZE)
        {
            /*Unsupported block size*/
            return NRF_ERROR_NOT_SUPPORTED;
        }
    
        if (MY_BLOCK_DEV_QSPI_ERASE_UNIT_SIZE % p_qspi_dev->qspi_bdev_config.block_size)
        {
            /*Unsupported block size*/
            return NRF_ERROR_NOT_SUPPORTED;
        }
        /* Calculate block device geometry.... */   
        uint32_t blk_size  = p_qspi_dev->qspi_bdev_config.block_size;
        uint32_t blk_count = FAT_END_ADDR / p_qspi_dev->qspi_bdev_config.block_size;
    
        if (!blk_count || (blk_count % MY_BD_BLOCKS_PER_ERASEUNIT(blk_size)))
        {
            return NRF_ERROR_NOT_SUPPORTED;
        }
    
        p_work->geometry.blk_size  = blk_size;
        p_work->geometry.blk_count = blk_count;
        p_work->p_context          = p_context;
        p_work->ev_handler         = ev_handler;
    
        if (p_work->ev_handler)
        {
            /*Asynchronous operation (simulation)*/
            const nrf_block_dev_event_t ev = {
                    NRF_BLOCK_DEV_EVT_INIT,
                    NRF_BLOCK_DEV_RESULT_SUCCESS,
                    NULL,
                    p_work->p_context
            };
    
            p_work->ev_handler(p_blk_dev, &ev);
        }
    
        return NRF_SUCCESS;
    }
    /*************************************************************************   
    **	function name:	block_dev_qspi_uninit 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    static ret_code_t my_block_dev_qspi_uninit(nrf_block_dev_t const * p_blk_dev)
    {
        ASSERT(p_blk_dev);
        my_block_dev_qspi_t const * p_qspi_dev =
                                     CONTAINER_OF(p_blk_dev, my_block_dev_qspi_t, block_dev);
        my_block_dev_qspi_work_t * p_work = p_qspi_dev->p_work;
    
    
        if (p_work->ev_handler)
        {
            /*Asynchronous operation*/
            const nrf_block_dev_event_t ev = {
                    NRF_BLOCK_DEV_EVT_UNINIT,
                    NRF_BLOCK_DEV_RESULT_SUCCESS,
                    NULL,
                    p_work->p_context
            };
    
            p_work->ev_handler(p_blk_dev, &ev);
        }
    
    
    //    nrf_drv_qspi_uninit();
    
        memset(p_work, 0, sizeof(my_block_dev_qspi_work_t));
        return NRF_SUCCESS;
    }
    
    /*************************************************************************   
    **	function name:	block_dev_qspi_read_req 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    static ret_code_t my_block_dev_qspi_read_req(nrf_block_dev_t const * p_blk_dev,
                                                 nrf_block_req_t const * p_blk)
    {
        ASSERT(p_blk_dev);
        ASSERT(p_blk);
        my_block_dev_qspi_t const * p_qspi_dev =
                                     CONTAINER_OF(p_blk_dev, my_block_dev_qspi_t, block_dev);
        my_block_dev_qspi_work_t *  p_work = p_qspi_dev->p_work;
    
        ret_code_t ret = NRF_SUCCESS;
    
        if ((p_blk->blk_id + p_blk->blk_count) > p_work->geometry.blk_count)
        {
           return NRF_ERROR_INVALID_ADDR;
        }
    
        uint32_t read_size = p_blk->blk_count * p_work->geometry.blk_size;
        uint32_t read_addr = p_blk->blk_id * p_work->geometry.blk_size;
        
        QSPI_flash_read(p_blk->p_buff, read_size, read_addr);
    
        if (p_work->ev_handler)
        {
            const nrf_block_dev_event_t ev = {
                    NRF_BLOCK_DEV_EVT_BLK_READ_DONE,
                    NRF_BLOCK_DEV_RESULT_SUCCESS,
                    NULL,
                    p_work->p_context
            };
    
            p_work->ev_handler(&p_qspi_dev->block_dev, &ev);
        }
    
        return ret;
    }
    /*************************************************************************   
    **	function name:	block_dev_qspi_read_req 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    static ret_code_t my_block_dev_qspi_write_req(nrf_block_dev_t const * p_blk_dev,
                                                  nrf_block_req_t const * p_blk)
    {
        ASSERT(p_blk_dev);
        ASSERT(p_blk);
        my_block_dev_qspi_t const * p_qspi_dev =
                                     CONTAINER_OF(p_blk_dev, my_block_dev_qspi_t, block_dev);
        my_block_dev_qspi_work_t *  p_work = p_qspi_dev->p_work;
    
        ret_code_t ret = NRF_SUCCESS;
    
        if ((p_blk->blk_id + p_blk->blk_count) > p_work->geometry.blk_count)
        {
            return NRF_ERROR_INVALID_ADDR;
        }
    
        uint32_t write_size = p_blk->blk_count * p_work->geometry.blk_size;
        uint32_t write_addr = p_blk->blk_id * p_work->geometry.blk_size;
        
        QSPI_flash_write(p_blk->p_buff, write_size, write_addr);
    
        if (p_work->ev_handler)
        {
            const nrf_block_dev_event_t ev = {
                    NRF_BLOCK_DEV_EVT_BLK_WRITE_DONE,
                    NRF_BLOCK_DEV_RESULT_SUCCESS,
                    NULL,
                    p_work->p_context
            };
    
            p_work->ev_handler(&p_qspi_dev->block_dev, &ev);
        }
    
        return ret;
    }
    
    static ret_code_t my_block_dev_qspi_ioctl(nrf_block_dev_t const * p_blk_dev,
                                              nrf_block_dev_ioctl_req_t req,
                                              void * p_data)
    {
        ASSERT(p_blk_dev);
        my_block_dev_qspi_t const * p_qspi_dev =
                                     CONTAINER_OF(p_blk_dev, my_block_dev_qspi_t, block_dev);
        my_block_dev_qspi_work_t *  p_work = p_qspi_dev->p_work;
    
        switch (req)
        {
            case NRF_BLOCK_DEV_IOCTL_REQ_CACHE_FLUSH:
            {
                bool * p_flushing = p_data;
                
                *p_flushing = false;
                return NRF_SUCCESS;
            }
            case NRF_BLOCK_DEV_IOCTL_REQ_INFO_STRINGS:
            {
                if (p_data == NULL)
                {
                    return NRF_ERROR_INVALID_PARAM;
                }
    
                nrf_block_dev_info_strings_t const * * pp_strings = p_data;
                *pp_strings = &p_qspi_dev->info_strings;
                return NRF_SUCCESS;
            }
            default:
                break;
        }
    
        return NRF_ERROR_NOT_SUPPORTED;
    }
    
    static nrf_block_dev_geometry_t const * my_block_dev_qspi_geometry(nrf_block_dev_t const * p_blk_dev)
    {
        ASSERT(p_blk_dev);
        my_block_dev_qspi_t const * p_qspi_dev =
                                     CONTAINER_OF(p_blk_dev, my_block_dev_qspi_t, block_dev);
        my_block_dev_qspi_work_t const * p_work = p_qspi_dev->p_work;
    
        return &p_work->geometry;
    }
    
    const nrf_block_dev_ops_t my_block_device_qspi_ops = {
            .init      = my_block_dev_qspi_init,
            .uninit    = my_block_dev_qspi_uninit,
            .read_req  = my_block_dev_qspi_read_req,
            .write_req = my_block_dev_qspi_write_req,
            .ioctl     = my_block_dev_qspi_ioctl,
            .geometry  = my_block_dev_qspi_geometry,
    };
    
    /** @} */
    
    
    #define __QSPI_DRIVER_INC__
    #include "my_freeRTOS_config.h"
    
    #include "nrf_gpio.h"
    #include "nrf_delay.h"
    
    #include "FreeRTOS_IO.h"
    #include "QSPI_driver.h"
    #include "nrf_drv_qspi.h"
    /***********************************************************************
    *                               macro define
    ************************************************************************
    */
    //ָ���
    #define W25X_WriteEnable		0x06 
    #define W25X_WriteDisable		0x04 
    #define W25X_ReadStatusReg		0x05 
    #define W25X_WriteStatusReg		0x01 
    #define W25X_ReadData			0x03 
    #define W25X_FastReadData		0x0B 
    #define W25X_FastReadDual		0x3B 
    #define W25X_PageProgram		0x02 
    #define W25X_BlockErase			0xD8 
    #define W25X_SectorErase		0x20 
    #define W25X_ChipErase			0xC7 
    
    #define W25X_PowerDown			0xB9 
    #define W25X_ReleasePowerDown	0xAB 
    
    /*
    QSPI��ָ�������read ID,�ʲ���һ������ȥ��ȡ
    ��Ϊ���������ȡID����Ҫд����Ч���ݣ�Ӳ���в�֧��
    */
    #define W25X_DeviceID			0x90 
    #define W25X_DeviceID_DUAL_IO   0x92
    #define W25X_DeviceID_QUAD_IO   0x94
    
    #define W25X_ManufactDeviceID	0x90 
    
    /*
    jedec�������豸ID��Ϣ�����������ڻ�ȡIDֻ�ж�������д�����Կ���ʹ��
    nrfx_qspi_cinstr_xfer����ȡ �������������ID���deviceID��1
    */
    #define W25X_JedecDeviceID		0x9F 
    
    
    #define QSPI_STD_CMD_WRSR       0x01
    #define QSPI_STD_CMD_RSTEN      0x66
    #define QSPI_STD_CMD_RST        0x99
    
    #define QSPI_TX_RX_BUF_SIZE     4096//4K
    
    #define QSPI_MaxDelay           500
    #define QSPI_INSTANCE_ID        0
    
    
    #define FLASH_SECTOR_SIZE       0x1000//4K
    
    #define CALCULATE_SECTOR_BASE_ADDR(addr)   ((addr) >> 12)
    #define CALCULATE_ADDR_BASE_SECTOR(sector) ((sector) << 12)
    
    #if QSPI_TX_RX_BUF_SIZE != FLASH_SECTOR_SIZE
    #error QSPI_TX_RX_BUF_SIZE need equal to FLASH_SECTOR_SIZE!!!
    #endif
    /***********************************************************************
    *                               my type
    ************************************************************************
    */
    typedef struct
    {
        bool dirty;
        uint32_t eunit_idx;
    }qspi_work_t;
    
    /***********************************************************************
    *                               extern variable
    ************************************************************************
    */
    
    /***********************************************************************
    *                               extern function
    ************************************************************************
    */
    
    /***********************************************************************
    *                               global variable
    ************************************************************************
    */
     
    /***********************************************************************
    *                               global function
    ************************************************************************
    */
    
    /***********************************************************************
    *                               static variable
    ************************************************************************
    */
    static Peripheral_Descriptor_t xQSPI_Port = NULL;
    
    //��flash��д���buf��ַ���������ֽڶ��룬
    //��Ϊд�����������buf����ת��Ϊuint32_t���ͣ�
    //���û�ж��룬���ݲ����ͻ����
    static uint32_t m32_qspi_buf[QSPI_TX_RX_BUF_SIZE/4];
    static uint8_t  *const pc_qspi_buf = (uint8_t *)m32_qspi_buf;
    
    
    static uint32_t m_read_addr;
    static uint32_t m_write_addr;
    
    static nrfx_qspi_handler_t m_qspi_handler;
    
    static qspi_work_t m_qspi_work;
    
    /***********************************************************************
    *                               static function
    ************************************************************************
    */
    static void nrf52_qspi_set_mode(uint8_t trans_mode);
    
    /***********************************************************************
    *                                   end
    ************************************************************************
    */
    /*************************************************************************   
    **	function name:	 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    static void QSPI_driver_handler(nrfx_qspi_evt_t event, void * p_context)
    {
        m_qspi_handler(event, p_context);
    }
    /*************************************************************************   
    **	function name:	 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    void nrf52_QSPI_init(uint32_t cPeripheralNumber , void *handler)
    {
        nrfx_err_t err;    
    
        cPeripheralNumber = cPeripheralNumber;
    
        nrfx_qspi_config_t qspi_conf = NRFX_QSPI_DEFAULT_CONFIG;
    
        //��ʼ��gpio pin
        qspi_conf.pins.csn_pin = QSPI_CS_PIN_MAP;
        qspi_conf.pins.sck_pin = QSPI_CLK_PIN_MAP;
        qspi_conf.pins.io0_pin = QSPI_DIO0_PIN_MAP;
        qspi_conf.pins.io1_pin = QSPI_DIO1_PIN_MAP;
        qspi_conf.pins.io2_pin = QSPI_DIO2_PIN_MAP;
        qspi_conf.pins.io3_pin = QSPI_DIO3_PIN_MAP;
    
        qspi_conf.prot_if.readoc    = NRF_QSPI_READOC_READ4O;//NRF_QSPI_READOC_FASTREAD;//
        qspi_conf.prot_if.writeoc   = NRF_QSPI_WRITEOC_PP4O;//NRF_QSPI_WRITEOC_PP;//
        qspi_conf.prot_if.addrmode  = NRF_QSPI_ADDRMODE_24BIT;
        qspi_conf.prot_if.dpmconfig = false;
    
        qspi_conf.phy_if.sck_delay = 1;
        qspi_conf.phy_if.dpmen     = false;
        qspi_conf.phy_if.spi_mode  = NRF_QSPI_MODE_0;
        qspi_conf.phy_if.sck_freq  = NRF_QSPI_FREQ_32MDIV1;
    
        qspi_conf.irq_priority     = NRFX_QSPI_CONFIG_IRQ_PRIORITY;//6
    
    
        err = nrf_drv_qspi_init(&qspi_conf, QSPI_driver_handler, NULL);
        APP_ERROR_CHECK(err);
    
        m_qspi_handler = (nrfx_qspi_handler_t)handler;
    
        //�տ���ʹ��polling mode
        nrf52_qspi_set_mode(ioctlUSE_POLLED_TX);
        
    }
    /*************************************************************************   
    **	function name:	 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    static void nrf52_qspi_set_mode(uint8_t trans_mode)
    {
        nrfx_err_t err;    
        if((trans_mode == ioctlUSE_POLLED_TX) || (trans_mode == ioctlUSE_POLLED_RX))
        {
            err = nrf_drv_qspi_set_handler(NULL);
            APP_ERROR_CHECK(err);
        }
        else
        {
            if(trans_mode <= ioctlUSE_ZERO_COPY_TXRX)
            {
                err = nrf_drv_qspi_set_handler(QSPI_driver_handler);
                APP_ERROR_CHECK(err);
        
                FreeRTOS_ioctl(xQSPI_Port, trans_mode, NULL);
            }
        }
    }
    /*************************************************************************   
    **	function name:	 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    void nrf52_qspi_int_config(const uint32_t ulPeripheralNumber, bool enable)
    {
        //do nothing...
    #if 0
    //    nrfx_err_t err = NRFX_SUCCESS;
    
    //    if(ulPeripheralNumber == QSPI_INSTANCE_ID)
    //    {
    //        err = nrf_drv_qspi_int_enable_ctrl(enable);
    //        APP_ERROR_CHECK(err);
    //    }
    #error must not do this...
    #endif
    }
    /*************************************************************************   
    **	function name:	 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    void nrf52_qspi_set_frequency(const uint32_t ulPeripheralNumber, uint32_t freq)
    {
        nrf_drv_qspi_set_frequency(freq);
    }
    /*************************************************************************   
    **	function name:	 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    void nrf52_qspi_set_int_priority(const uint32_t ulPeripheralNumber, uint32_t priority)
    {
        if(ulPeripheralNumber == QSPI_INSTANCE_ID)
        {
        }
    }
    
    /*************************************************************************   
    **	function name:	 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    void nrf52_qspi_zero_copy_TX(const uint32_t ulPeripheralNumber, const uint8_t *p_tx_data, uint32_t length)
    {
        nrfx_err_t err;
    
        //���ȱ�����4�ı���
    	configASSERT( (length&0x03) == 0);
        //buf��ַ�������ֽڶ���
    	configASSERT( ((uint32_t)p_tx_data&0x03) == 0);
        
        err = nrfx_qspi_write(p_tx_data, length, m_write_addr);
        APP_ERROR_CHECK(err);
    //	#ifdef __QSPI_DRIVRE_DEBUG__
    //    printf("write err:%#x, %#x, %d\r\n", err, m_write_addr, length);
    //    #endif
    }
    /*************************************************************************   
    **	function name:	 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    void nrf52_qspi_zero_copy_RX(const uint32_t ulPeripheralNumber, uint8_t *p_rx_data, uint32_t length)
    {
        nrfx_err_t err;
    
        //���ȱ�����4�ı���
    	configASSERT( (length&0x03) == 0);
        //buf��ַ�������ֽڶ���
    	configASSERT(((uint32_t)p_rx_data&0x03) == 0);
        
        err = nrfx_qspi_read(p_rx_data, length, m_read_addr);
        APP_ERROR_CHECK(err);
    }
    
    
    /*************************************************************************   
    **	function name:	 
    **	description:�˺������ͻ������ִ��Ϊpolled ģʽ	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    static void flash_wakeup(void)
    {
        nrfx_err_t err;
        err = nrf_drv_qspi_cinstr_quick_send(W25X_ReleasePowerDown, NRF_QSPI_CINSTR_LEN_1B, NULL);
    //    #ifdef __QSPI_DRIVRE_DEBUG__
    //    printf("110err:%#x\r\n", err);
    //    #endif
    
        APP_ERROR_CHECK(err);
    }
    /*************************************************************************   
    **	function name:	 
    **	description:�˺��������������ִ��Ϊpolled ģʽ	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    static void flash_sleep(void)
    {
        nrfx_err_t err;
        err= nrf_drv_qspi_cinstr_quick_send(W25X_PowerDown, NRF_QSPI_CINSTR_LEN_1B, NULL);
        APP_ERROR_CHECK(err);
    }
    
    /*************************************************************************   
    **	function name:	 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    static uint16_t read_flash_id(void)
    {
        nrfx_err_t err;
        uint16_t   jedec_id;
    
        nrf_qspi_cinstr_conf_t cinstr_cfg = 
        {
            .opcode    = W25X_JedecDeviceID,
            .length    = NRF_QSPI_CINSTR_LEN_4B,
            .io2_level = true,
            .io3_level = true,
            .wipwait   = true,
            .wren      = true
        };
        
        flash_wakeup();    
        err = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, pc_qspi_buf);
    //    #ifdef __QSPI_DRIVRE_DEBUG__
    //    printf("111err:%#x\r\n", err);
    //    #endif
        APP_ERROR_CHECK(err);
        flash_sleep();
    
        jedec_id = (pc_qspi_buf[0] <<8) | pc_qspi_buf[2];
    
        #ifdef __QSPI_DRIVRE_DEBUG__
        for(uint8_t i=0; i<6; i++)
            printf("QSPI read id:%#x\r\n", pc_qspi_buf[i]);
        #endif
    
        return (jedec_id-1);
    }
    
    /*************************************************************************   
    **	function name:	 
    **	description:	�������У���֪��ɶ��˼                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    static void configure_memory(void)
    {
        uint8_t temporary = 0x40;
        uint32_t err_code;
        nrf_qspi_cinstr_conf_t cinstr_cfg = {
            .opcode    = QSPI_STD_CMD_RSTEN,
            .length    = NRF_QSPI_CINSTR_LEN_1B,
            .io2_level = true,
            .io3_level = true,
            .wipwait   = true,
            .wren      = true
        };
    
        // Send reset enable
        err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
        APP_ERROR_CHECK(err_code);
    
        // Send reset command
        cinstr_cfg.opcode = QSPI_STD_CMD_RST;
        err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
        APP_ERROR_CHECK(err_code);
    
        // Switch to qspi mode
        cinstr_cfg.opcode = QSPI_STD_CMD_WRSR;
        cinstr_cfg.length = NRF_QSPI_CINSTR_LEN_2B;
        err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, &temporary, NULL);
        APP_ERROR_CHECK(err_code);
    }
    /*************************************************************************   
    **	function name:	 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    void QSPI_driver_init(void * xQSPI_PortIn)
    {    
    	configASSERT( xQSPI_PortIn );
        
        if(xQSPI_Port == NULL)
        {
            xQSPI_Port = (Peripheral_Descriptor_t*)xQSPI_PortIn;
        }
    
        configure_memory();    
    
        //���뽫��ID������ǰ��
        W25QXX.W25QXX_TYPE = read_flash_id();
    
        W25QXX.W25QXX_error = false;
        
        switch(W25QXX.W25QXX_TYPE)
        {
            case W25Q80:
                
                W25Q80_CAPACITY(W25QXX.capacity.physical_sector_size, 
                                W25QXX.capacity.physical_sector_count,
                                W25QXX.capacity.physical_block_count);
                break;
    
            case W25Q16:
                
                W25Q16_CAPACITY(W25QXX.capacity.physical_sector_size, 
                                W25QXX.capacity.physical_sector_count,
                                W25QXX.capacity.physical_block_count);
                break;
    
            case W25Q32:
                
                W25Q32_CAPACITY(W25QXX.capacity.physical_sector_size, 
                                W25QXX.capacity.physical_sector_count,
                                W25QXX.capacity.physical_block_count);
                break;
    
            case W25Q64:
                
                W25Q64_CAPACITY(W25QXX.capacity.physical_sector_size, 
                                W25QXX.capacity.physical_sector_count,
                                W25QXX.capacity.physical_block_count);
                break;
    
            case W25Q128:
                
                W25Q128_CAPACITY(W25QXX.capacity.physical_sector_size, 
                                 W25QXX.capacity.physical_sector_count,
                                 W25QXX.capacity.physical_block_count);
                break;
    
            default:
                
                W25QXX.W25QXX_error = true;
                break;
        }
        
        if(W25QXX.W25QXX_error)
        {
            #ifdef __QSPI_DRIVRE_DEBUG__
            printf("external flash is error!!!\r\n");
            #endif
            return;
        }
    
        m_qspi_work.dirty = true;
        m_qspi_work.eunit_idx = 0xFFFFFFFF;
        
        
        nrf52_qspi_set_mode(ioctlUSE_ZERO_COPY_TXRX);
        
    #if 0
        nrf_gpio_cfg_output(I2C_SDA_PIN_MAP);
        nrf_gpio_pin_clear(I2C_SDA_PIN_MAP);
    
    
    //    for(uint8_t i=0; i<5; i++)
        {
            uint8_t buf[10] = {1,2,3,4,5,6,7,8,9,10};
    
            for(uint16_t  i=0; i<10; i++)
            {
                buf[i] = i;
            }
            QSPI_flash_write(buf, 10,  4090);
    		
    //		#ifdef __QSPI_DRIVRE_DEBUG__
    //        printf("QSPI write:");
    //        for(uint16_t i=0; i<10; i++)
    //        {
    //            printf("%#x ", buf[i]);
    //        }
    //        printf("\r\n");
    //        #endif
        
            QSPI_flash_read(buf, 10, 4090);
    
            
            #ifdef __QSPI_DRIVRE_DEBUG__
            printf("buf a:%#x, m_test_addr:%#x\r\n", buf, buf);
            printf("QSPI read:");
            for(uint16_t i=0; i<10; i++)
            {
                if((i & 0x0f) == 0)
                    printf("\r\n");
                
                printf("%#4x\t", buf[i]);
            }
            printf("\r\n");
            #endif
        }    
    #endif
    }
    
    /*************************************************************************   
    **	function name:	 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    void QSPI_flash_write(const void *p_tx_buf, uint32_t size, uint32_t addr)
    {
        const uint8_t *p_buffer;
        uint32_t i, secpos, secoff, secremain;
    
        if(W25QXX.W25QXX_error)
        {
            return;
        }
    
        p_buffer = (const uint8_t *)p_tx_buf;
    
        secpos = addr >> 12;//������ַ  
        secoff = addr & (FLASH_SECTOR_SIZE-1);//�������ڵ�ƫ��
        secremain = FLASH_SECTOR_SIZE - secoff;//����ʣ��ռ��С  
        
     	if(size <= secremain)
        {
            secremain = size;//û�п�����
        }
        
        if(FreeRTOS_ioctl(xQSPI_Port, ioctlOBTAIN_WRITE_MUTEX, (void *)QSPI_MaxDelay) == pdTRUE)
        {
            flash_wakeup();
            
            while(1) 
            {	
                if(m_qspi_work.dirty || (m_qspi_work.eunit_idx != secpos))
                {
                    if(FreeRTOS_ioctl(xQSPI_Port, ioctlBATAIN_WRITE_BINARY_SEMPHR,(void *)QSPI_MaxDelay) == pdTRUE)
                    {
                        m_qspi_work.eunit_idx = secpos;
                        m_qspi_work.dirty     = false;
                        
                        m_read_addr = CALCULATE_ADDR_BASE_SECTOR(secpos);
                        FreeRTOS_read(xQSPI_Port, pc_qspi_buf, QSPI_TX_RX_BUF_SIZE);                
                        //�ȴ����ݶ�ȡ���
                        FreeRTOS_ioctl(xQSPI_Port, ioctlWAITBINARY_PREVIOUS_WRITE_COMPLETE, (void *)QSPI_MaxDelay); 
                    }
                }
                
                for(i=0;i<secremain;i++)//������
                {
                    if(pc_qspi_buf[secoff+i] != p_buffer[i])
                    {
                        break;//��Ҫ����      
                    }
                }
    
                //��Ҫд������ݺ���Ŀǰ�����������ȫ��ͬ������д��            
                if(i != secremain)
                {
                    for(i=0;i<secremain;i++)//������
                    {
                        if(pc_qspi_buf[secoff+i] != 0xFF)
                        {
                            break;//��Ҫ����      
                        }                        
                    }
                    if(i<secremain)//��Ҫ����
                    {
                        nrfx_qspi_erase(NRF_QSPI_ERASE_LEN_4KB, CALCULATE_ADDR_BASE_SECTOR(secpos)); //4K����
                        while(nrfx_qspi_mem_busy_check() != NRFX_SUCCESS)
                        {
                            vTaskDelay(40);//ץ���δ����(31-34ms֮��)
                        }                    
                    }
                    
                    for(i=0;i<secremain;i++)	   //����
                    {
                        pc_qspi_buf[i+secoff] = p_buffer[i];	  
                    }                
                    
                    if(FreeRTOS_ioctl(xQSPI_Port, ioctlBATAIN_WRITE_BINARY_SEMPHR,(void *)QSPI_MaxDelay) == pdTRUE)
                    {
                        m_write_addr = m_read_addr;
                        FreeRTOS_write(xQSPI_Port, pc_qspi_buf, QSPI_TX_RX_BUF_SIZE);                 
                        FreeRTOS_ioctl(xQSPI_Port, ioctlWAITBINARY_PREVIOUS_WRITE_COMPLETE, (void *)QSPI_MaxDelay);                     
                        
                        //�ȴ�������ϣ�Ȼ��ȴ�flash����ִ�����(1-2ms)
                        while(nrfx_qspi_mem_busy_check() != NRFX_SUCCESS);
                    }
                }            
                
                if(size == secremain)
                {
                    break;//������
                }
                else//д��δ����
                {
                    secpos++;//������ַ��1
                    secoff = 0;//ƫ��λ��Ϊ0  
                    
                    p_buffer += secremain; //ָ��ƫ��
                    addr     += secremain; //д��ַƫ��	   
                    size     -= secremain; //�ֽ����ݼ�
                    
                    if(size > FLASH_SECTOR_SIZE)
                    {
                        secremain = FLASH_SECTOR_SIZE; //��һ����������д����
                    }
                    else 
                    {
                        secremain = size;           //��һ����������д����
                    }
                }	 
            }
                
            flash_sleep();
            FreeRTOS_ioctl(xQSPI_Port, ioctlWAIT_PREVIOUS_WRITE_COMPLETE, (void *)QSPI_MaxDelay);
        }
    }
    
    /*************************************************************************   
    **	function name:	 
    **	description:	                  
    **	input para:		 
    **                  	
    **	return:			                                       
    **************************************************************************/
    void QSPI_flash_read(void *p_rx_buf, uint32_t size, uint32_t addr)
    {
        uint8_t  *p_buffer;
        uint32_t read_size, copy_size, rx_offset;
    
        if(W25QXX.W25QXX_error)
        {
            return;
        }
    
        p_buffer = (uint8_t *)p_rx_buf;
        
        if(FreeRTOS_ioctl(xQSPI_Port, ioctlOBTAIN_WRITE_MUTEX, (void *)QSPI_MaxDelay) == pdTRUE)
        {
            flash_wakeup(); 
    
            if((((uint32_t)p_rx_buf & 0x03) == 0) && ((size & 0x03) == 0))
            {
                if(FreeRTOS_ioctl(xQSPI_Port, ioctlBATAIN_WRITE_BINARY_SEMPHR, (void *)QSPI_MaxDelay) == pdTRUE)
                {
                    m_read_addr = addr;                
                    FreeRTOS_read(xQSPI_Port, p_rx_buf, size);
                    FreeRTOS_ioctl(xQSPI_Port, ioctlWAITBINARY_PREVIOUS_WRITE_COMPLETE, (void *)QSPI_MaxDelay); 
                }
            }
            else
            {
                rx_offset   = 0;
                m_qspi_work.dirty = true;
        
                while(size > 0)
                {
                    if(FreeRTOS_ioctl(xQSPI_Port, ioctlBATAIN_WRITE_BINARY_SEMPHR, (void *)QSPI_MaxDelay) == pdTRUE)
                    {
                        if(size > QSPI_TX_RX_BUF_SIZE)
                        {
                            read_size = QSPI_TX_RX_BUF_SIZE;
                            copy_size = QSPI_TX_RX_BUF_SIZE;
                            size     -= QSPI_TX_RX_BUF_SIZE;
                        }
                        else
                        {
                            read_size = ALIGN(size, 4);//4�ֽڶ���
                            copy_size = size;
                            size      = 0;
                        }
                    
                        m_read_addr = addr;                
                        FreeRTOS_read(xQSPI_Port, pc_qspi_buf, read_size);
                        FreeRTOS_ioctl(xQSPI_Port, ioctlWAITBINARY_PREVIOUS_WRITE_COMPLETE, (void *)QSPI_MaxDelay); 
        
                        memcpy(&p_buffer[rx_offset], pc_qspi_buf, copy_size);
                        rx_offset += copy_size;
                        addr      += read_size;
                    }
                }
            }           
                    
            flash_sleep();        
            FreeRTOS_ioctl(xQSPI_Port, ioctlWAIT_PREVIOUS_WRITE_COMPLETE, (void *)QSPI_MaxDelay);
        }
    }
    
    
    


Related