External Flash W25Q64 with USBD_MSC example on NRF52840

Hi All,

        I have an external flash W25Q64 on which I have to mount a file system. I am using USBD_MSC example of NRF52840. My main goal is to create a file inside the flash memory chip. And when USB is plugged into the device it should be seen as a drive on laptop/desktop and I should be able to access the file which was created in the flash chip.I have made the connections on the NRF52840 using the pin configurations below:-


#define SDC_SCK_PIN     (25) 
#define SDC_MOSI_PIN   (23) 
#define SDC_MISO_PIN   (24) 
#define SDC_CS_PIN       (15) 

I have also configured the details in nrf_serial_flash_params.c file. 

static const nrf_serial_flash_params_t m_sflash_params[] = {
    {    
        .read_id = { 0xEF,0x40,0x17 },
        .capabilities = 0x00,
        .size = 8 * 1024 * 1024,
        .erase_size = 2 * 1024,
        .program_size = 256,
    }
};

nrf_serial_flash_params_t const * nrf_serial_flash_params_get(const uint8_t * p_read_id)
{
    size_t i;

    for (i = 0; i < ARRAY_SIZE(m_sflash_params); ++i)
    {
        if (memcmp(m_sflash_params[i].read_id, p_read_id, sizeof(m_sflash_params[i].read_id)) == 0)
        {
            return &m_sflash_params[i];
        }
    }

    return NULL;
}

When I run the example , i get the following error:-

<info> app: Initializing disk 0 (QSPI)...

 <error> app: Disk initialization failed.

1) Will this example run for W25Q64 IC??

2) This example also includes FATfs files. Will it automatically create a file system on the external SPI W25Q64 flash memory??

3) Why am I getting "Disk initialization failed." error?

4) Inside sdk_config.h do I also need to enable pin configurations for Qspi??

5) How to test this application? Currently I am using NRF52840 DK and then connecting external spi flash to it. But for USB detection how will it work?

I have attached few files here to check for the configurations

04162.sdk_config.h

5287.W25Q64BV.PDF

Please help me to solve this issue. I need to solve this issue on an urgent basis. Your help will be appreciated. I have also attached W25Q64 datasheet for your reference.

Thanks & Regards,

Snehal

Parents
  • Hi,

    Take look at this case, where the configuration needed for this flash chip is discussed:
    RE: QSPI and w25q64fv flash

  • Hi Sigurd,

                  The thread you mentioned shows QSPI with flash directly. I am struggling for USB MSC code. Can you please help me with USB MSC code?

    Thanks & Regards,

    Snehal

  • Hi,

    Make sure that you are using the correct pins. And that the external chip have power. And maybe check with an Logic analyzer, e.g. Saleae logic or similar, that the SPI signals are as expected. 

  • Solved that issue. Basic SPI connection is happening now. But getting error for "Initializing disk 0 (SPI)...
    <error> app: Disk initialization failed.disk_state<1>"

    The flash chip I am using is AT25df041 which is 4Mbit  with following features:-

    what should be modified in the config??what size , erase size & program size should be set?

    static const nrf_serial_flash_params_t m_sflash_params[] = {

    .read_id = { 0x1F, 0x44, 0x02 },
    .capabilities = 0x00,
    .size = 8 * 1024 * 1024,
    .erase_size = 4 * 1024,
    .program_size = 256,
    }
    };

    Thanks,

    Snehal

  • Hi,

    sne_333 said:
    Solved that issue. Basic SPI connection is happening now.

    Great!

    sne_333 said:
    .size = 8 * 1024 * 1024,

    Try setting this to 524288

  • Tried this too but getting same error. Can you please help me with the files?

    /**
     * Copyright (c) 2016 - 2020, 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 "sdk_common.h"
    #if NRF_MODULE_ENABLED(NRF_BLOCK_DEV_SPI)
    #include "nrf_serial_flash_params.h"
    #include "nrf_block_dev_spi.h"
    #include "spi.h"
    #include "nrf_drv_spi.h"
    #include "nrfx_spi.h"
    #include <inttypes.h>
    
    /**@file
     *
     * @ingroup nrf_block_dev_spi
     * @{
     *
     * @brief This module implements block device API. It should be used as a reference block device.
     */
    
    #if NRF_BLOCK_DEV_SPI_CONFIG_LOG_ENABLED
    #define NRF_LOG_LEVEL       NRF_BLOCK_DEV_SPI_CONFIG_LOG_LEVEL
    #define NRF_LOG_INFO_COLOR  NRF_BLOCK_DEV_SPI_CONFIG_INFO_COLOR
    #define NRF_LOG_DEBUG_COLOR NRF_BLOCK_DEV_SPI_CONFIG_DEBUG_COLOR
    #else
    #define NRF_LOG_LEVEL       0
    #endif
    #include "nrf_log.h"
    
    #define SPI_STD_CMD_WRSR    0x01   /**< Write status register command*/
    #define SPI_STD_CMD_RSTEN   0x66   /**< Reset enable command*/
    #define SPI_STD_CMD_RST     0x99   /**< Reset command*/
    #define SPI_STD_CMD_READ_ID 0x9F   /**< Read ID command*/
    
    #define BD_PAGE_PROGRAM_SIZE 256    /**< Page program size (minimum block size)*/
    
    #define BD_ERASE_UNIT_INVALID_ID   0xFFFFFFFF /**< Invalid erase unit number*/
    #define BD_ERASE_UNIT_ERASE_VAL    0xFFFFFFFF /**< Erased memory value*/
    
    //static const nrf_drv_spi_t m_spi = NRF_DRV_SPI_INSTANCE(1);
    extern const nrf_drv_spi_t m_spi;
    
    static void spi_handler(nrf_drv_spi_evt_t event, void * p_context);
    
    /**
     * @brief Block to erase unit translation
     *
     * @param blk_id    Block index
     * @param blk_size  Block size
     * */
    #define BD_BLOCK_TO_ERASEUNIT(blk_id, blk_size)   \
        ((blk_id) * (blk_size)) / (NRF_BLOCK_DEV_SPI_ERASE_UNIT_SIZE)
    
    /**
     * @brief Blocks per erase unit
     *
     * @param blk_size  Block size
     * */
    #define BD_BLOCKS_PER_ERASEUNIT(blk_size)         \
        (NRF_BLOCK_DEV_SPI_ERASE_UNIT_SIZE / (blk_size))
    
    
    static ret_code_t block_dev_spi_eunit_write(nrf_block_dev_spi_t const * p_spi_dev,
                                                 nrf_block_req_t * p_blk_left);
    
    nrfx_err_t nrf_drv_spi_read(void *   p_rx_buffer,
                              size_t   rx_buffer_length,
                              uint32_t src_address);
    
    nrfx_err_t nrf_drv_spi_write(void const * p_tx_buffer,size_t tx_buffer_length,uint32_t dst_address);
    
    static void block_dev_spi_read_from_eunit(nrf_block_dev_spi_t const * p_spi_dev)
    {
        nrf_block_dev_spi_work_t const * p_work = p_spi_dev->p_work;
    
        /*In write-back mode data that we read might not be the same as in erase unit buffer*/
        uint32_t eunit_start = BD_BLOCK_TO_ERASEUNIT(p_work->req.blk_id,
                                                     p_work->geometry.blk_size);
    
        uint32_t eunit_end = BD_BLOCK_TO_ERASEUNIT(p_work->req.blk_id + p_work->req.blk_count,
                                                   p_work->geometry.blk_size);
    
        if ((eunit_start > p_work->erase_unit_idx) || (eunit_end < p_work->erase_unit_idx))
        {
            /*Do nothing. Read request doesn't hit current cached erase unit*/
            return;
        }
    
        /*Case 1: Copy data from start erase unit*/
        if (eunit_start == p_work->erase_unit_idx)
        {
             size_t blk = p_work->req.blk_id %
                          BD_BLOCKS_PER_ERASEUNIT(p_work->geometry.blk_size);
             size_t cnt = BD_BLOCKS_PER_ERASEUNIT(p_work->geometry.blk_size) - blk;
             size_t off = p_work->geometry.blk_size * blk;
    
             if (cnt > p_work->req.blk_count)
             {
                 cnt = p_work->req.blk_count;
             }
    
             memcpy(p_work->req.p_buff,
                    p_work->p_erase_unit_buff + off,
                    cnt * p_work->geometry.blk_size);
    
             return;
        }
    
        /*Case 2: Copy data from end erase unit*/
        if (eunit_end == p_work->erase_unit_idx)
        {
             size_t cnt = (p_work->req.blk_id + p_work->req.blk_count) %
                           BD_BLOCKS_PER_ERASEUNIT(p_work->geometry.blk_size);
             size_t off = (p_work->erase_unit_idx * BD_BLOCKS_PER_ERASEUNIT(p_work->geometry.blk_size) -
                           p_work->req.blk_id) * p_work->geometry.blk_size;
    
             if (cnt > p_work->req.blk_count)
             {
                 cnt = p_work->req.blk_count;
             }
    
             memcpy((uint8_t *)p_work->req.p_buff + off,
                    p_work->p_erase_unit_buff,
                    cnt * p_work->geometry.blk_size);
    
             return;
        }
    
        /*Case 3: Copy data from  eunit_start < p_work->erase_unit_idx < eunit_end*/
        size_t off = (p_work->erase_unit_idx * BD_BLOCKS_PER_ERASEUNIT(p_work->geometry.blk_size) -
                      p_work->req.blk_id) * p_work->geometry.blk_size;
    
        memcpy((uint8_t *)p_work->req.p_buff + off,
               p_work->p_erase_unit_buff,
               NRF_BLOCK_DEV_SPI_ERASE_UNIT_SIZE);
    }
    
    /**
     * @brief Active SPI block device handle. Only one instance.
     * */
    static nrf_block_dev_spi_t const * m_active_spi_dev;
    
    static void spi_handler(nrf_drv_spi_evt_t event, void * p_context)
    {
        if (m_active_spi_dev != p_context)
        {
            return;
        }
    
        nrf_block_dev_spi_t const * p_spi_dev = p_context;
        nrf_block_dev_spi_work_t *  p_work = p_spi_dev->p_work;
        nrf_block_req_t * p_blk_left = &p_work->left_req;
    
        switch (p_work->state)
        {
            case NRF_BLOCK_DEV_SPI_STATE_READ_EXEC:
            {
                if (p_work->writeback_mode)
                {
                    block_dev_spi_read_from_eunit(p_spi_dev);
                }
    
                p_work->state = NRF_BLOCK_DEV_SPI_STATE_IDLE;
                if (p_work->ev_handler)
                {
                    const nrf_block_dev_event_t ev = {
                            NRF_BLOCK_DEV_EVT_BLK_READ_DONE,
                            NRF_BLOCK_DEV_RESULT_SUCCESS,
                            &p_work->req,
                            p_work->p_context
                    };
    
                    p_work->ev_handler(&p_spi_dev->block_dev, &ev);
                }
    
                break;
            }
            case NRF_BLOCK_DEV_SPI_STATE_EUNIT_LOAD:
            {
                ret_code_t ret;
                uint32_t erase_unit = BD_BLOCK_TO_ERASEUNIT(p_blk_left->blk_id,
                                                            p_work->geometry.blk_size);
                UNUSED_VARIABLE(erase_unit);
                ASSERT(erase_unit == p_work->erase_unit_idx);
    
                /* Check if block is in erase unit buffer*/
                ret = block_dev_spi_eunit_write(p_spi_dev, p_blk_left);
                ASSERT(ret == NRF_SUCCESS);
                UNUSED_VARIABLE(ret);
                break;
            }
            case NRF_BLOCK_DEV_SPI_STATE_WRITE_ERASE:
            case NRF_BLOCK_DEV_SPI_STATE_WRITE_EXEC:
            {
                /*Clear last programmed block*/
                uint32_t block_to_program = __CLZ(__RBIT(p_work->erase_unit_dirty_blocks));
    
                if (p_work->state == NRF_BLOCK_DEV_SPI_STATE_WRITE_EXEC)
                {
                    p_work->erase_unit_dirty_blocks ^= 1u << block_to_program;
                }
    
                if (p_work->erase_unit_dirty_blocks == 0)
                {
                    if (p_work->left_req.blk_count)
                    {
                        /*Load next erase unit*/
                        ret_code_t ret;
                        uint32_t eunit = BD_BLOCK_TO_ERASEUNIT(p_blk_left->blk_id,
                                                               p_work->geometry.blk_size);
    
                        p_work->erase_unit_idx = eunit;
                        p_work->state = NRF_BLOCK_DEV_SPI_STATE_EUNIT_LOAD;
    
                        ret = nrf_drv_spi_read(p_work->p_erase_unit_buff,
                                                NRF_BLOCK_DEV_SPI_ERASE_UNIT_SIZE,
                                                p_work->erase_unit_idx *
                                                NRF_BLOCK_DEV_SPI_ERASE_UNIT_SIZE);
                        UNUSED_VARIABLE(ret);
    
                        break;
                    }
    
                    /*All blocks are programmed. Call event handler if required.*/
                    p_work->state = NRF_BLOCK_DEV_SPI_STATE_IDLE;
                    if (p_work->ev_handler && !p_work->cache_flushing)
                    {
                        const nrf_block_dev_event_t ev = {
                                NRF_BLOCK_DEV_EVT_BLK_WRITE_DONE,
                                NRF_BLOCK_DEV_RESULT_SUCCESS,
                                &p_work->req,
                                p_work->p_context
                        };
    
                        p_work->ev_handler(&p_spi_dev->block_dev, &ev);
                    }
    
                    p_work->cache_flushing = false;
                    break;
                }
    
                /*Get next block to program from program mask*/
                block_to_program = __CLZ(__RBIT(p_work->erase_unit_dirty_blocks));
                uint32_t dst_address = (p_work->erase_unit_idx * NRF_BLOCK_DEV_SPI_ERASE_UNIT_SIZE) +
                                       (block_to_program * p_work->geometry.blk_size);
    
                const void * p_src_address = p_work->p_erase_unit_buff +
                                             block_to_program * p_work->geometry.blk_size;
    
                p_work->state = NRF_BLOCK_DEV_SPI_STATE_WRITE_EXEC;
                ret_code_t ret = nrf_drv_spi_write(p_src_address,
                                                    p_work->geometry.blk_size,
                                                    dst_address);
                UNUSED_VARIABLE(ret);
                break;
            }
            default:
                ASSERT(0);
                break;
        }
    }
    
    static void wait_for_idle(nrf_block_dev_spi_t const * p_spi_dev)
    {
        nrf_block_dev_spi_work_t * p_work = p_spi_dev->p_work;
        while (p_work->state != NRF_BLOCK_DEV_SPI_STATE_IDLE)
        {
            __WFI();
        }
    }
    
    
    extern uint8_t spi_tx_buf[4];
    extern uint8_t spi_rx_buf[4];
    extern volatile bool spi_xfer_done;
    
    static ret_code_t block_dev_spi_init(nrf_block_dev_t const * p_blk_dev,
                                          nrf_block_dev_ev_handler ev_handler,
                                          void const * p_context)
    {
        ASSERT(p_blk_dev);
        nrf_block_dev_spi_t const *  p_spi_dev =
                                      CONTAINER_OF(p_blk_dev, nrf_block_dev_spi_t, block_dev);
        nrf_block_dev_spi_work_t *   p_work = p_spi_dev->p_work;
        nrf_drv_spi_config_t const * p_spi_cfg = &p_spi_dev->spi_bdev_config.spi_config;
    
        ret_code_t ret = NRF_SUCCESS;
    
        NRF_LOG_INST_DEBUG(p_spi_dev->p_log, "Init");
    
        if (p_spi_dev->spi_bdev_config.block_size % BD_PAGE_PROGRAM_SIZE)
        {
            /*Unsupported block size*/
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "Unsupported block size because of program page size");
            return NRF_ERROR_NOT_SUPPORTED;
        }
    
        if (NRF_BLOCK_DEV_SPI_ERASE_UNIT_SIZE % p_spi_dev->spi_bdev_config.block_size)
        {
            /*Unsupported block size*/
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "Unsupported block size because of erase unit size");
            return NRF_ERROR_NOT_SUPPORTED;
        }
    
        if (m_active_spi_dev)
        {
            /* SPI instance is BUSY*/
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "Cannot init because SPI is busy");
            return NRF_ERROR_BUSY;
        }
        
         nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
    
         spi_config.ss_pin = SPI_SS_PIN;
         spi_config.miso_pin = SPI_MISO_PIN;
         spi_config.mosi_pin = SPI_MOSI_PIN;
         spi_config.sck_pin =  SPI_SCK_PIN;
         spi_config.frequency = NRF_DRV_SPI_FREQ_4M;
    
        spi_config.mode = NRF_DRV_SPI_MODE_3;
        spi_config.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST;
        spi_config.irq_priority = APP_IRQ_PRIORITY_LOW;
    
        // APP_ERROR_CHECK(nrf_drv_spi_init(&m_spi,&spi_config,spi_event_handler,NULL));
        //.. APP_ERROR_CHECK(nrf_drv_spi_init(&m_spi,&spi_config,spi_handler,(void *)p_blk_dev));
    
    
        ret = nrf_drv_spi_init(&m_spi,p_spi_cfg, spi_handler, (void *)p_blk_dev);
        if (ret != NRF_SUCCESS)
        {
           // NRF_LOG_INST_ERROR(p_spi_dev->p_log, "SPI init error: %"PRIu32"", ret);
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "SPI init error: %"PRIu32"", ret);
            return ret;
        }
    
        nrf_spi_cinstr_conf_t cinstr_cfg = {
            .opcode    = SPI_STD_CMD_RSTEN,
            .length    = NRF_SPI_CINSTR_LEN_1B,
            .io2_level = true,
            .io3_level = true,
            .wipwait   = true,
            .wren      = true
        };
    
       spi_tx_buf[0] = 0x9f;
       spi_tx_buf[1] = 0xFF;
       spi_tx_buf[2] = 0xFF;
       spi_tx_buf[3] = 0xFF;
       spi_xfer_done = false;
         
       nrf_gpio_pin_set(SPI_HOLD);
       nrf_gpio_pin_clear(SPI_SS_PIN);
    
       APP_ERROR_CHECK(nrf_drv_spi_transfer(&m_spi,spi_tx_buf,4,spi_rx_buf,4));
    
       while(spi_xfer_done == false){};
    
       nrf_gpio_pin_set(SPI_SS_PIN);
       nrf_gpio_pin_clear(SPI_HOLD);
    
    
        nrf_serial_flash_params_t const * serial_flash_id = nrf_serial_flash_params_get(spi_rx_buf);
    
        if (!serial_flash_id)
        {
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "SPI FLASH not supported");
            return NRF_ERROR_NOT_SUPPORTED;
        }
    
        if (serial_flash_id->erase_size != NRF_BLOCK_DEV_SPI_ERASE_UNIT_SIZE)
        {
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "SPI FLASH erase unit size not supported");
            return NRF_ERROR_NOT_SUPPORTED;
        }
    
        /* Calculate block device geometry.... */
        uint32_t blk_size = p_spi_dev->spi_bdev_config.block_size;
        uint32_t blk_count = serial_flash_id->size / p_spi_dev->spi_bdev_config.block_size;
    
        if (!blk_count || (blk_count % BD_BLOCKS_PER_ERASEUNIT(blk_size)))
        {
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "SPI FLASH block size not supported");
            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;
    
        p_work->state = NRF_BLOCK_DEV_SPI_STATE_IDLE;
        p_work->erase_unit_idx = BD_ERASE_UNIT_INVALID_ID;
        p_work->writeback_mode =  (p_spi_dev->spi_bdev_config.flags &
                                   NRF_BLOCK_DEV_SPI_FLAG_CACHE_WRITEBACK) != 0;
        m_active_spi_dev = p_spi_dev;
    
        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;
    }
    
    static ret_code_t block_dev_spi_uninit(nrf_block_dev_t const * p_blk_dev)
    {
        ASSERT(p_blk_dev);
        nrf_block_dev_spi_t const * p_spi_dev =
                                     CONTAINER_OF(p_blk_dev, nrf_block_dev_spi_t, block_dev);
        nrf_block_dev_spi_work_t * p_work = p_spi_dev->p_work;
    
        NRF_LOG_INST_DEBUG(p_spi_dev->p_log, "Uninit");
    
        if (m_active_spi_dev != p_spi_dev)
        {
            /* SPI instance is BUSY*/
            return NRF_ERROR_BUSY;
        }
    
        if (p_work->state != NRF_BLOCK_DEV_SPI_STATE_IDLE)
        {
            /* Previous asynchronous operation in progress*/
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "Cannot uninit because SPI is busy");
            return NRF_ERROR_BUSY;
        }
    
        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);
        }
    
        p_work->state = NRF_BLOCK_DEV_SPI_STATE_DISABLED;
        nrf_drv_spi_uninit(&m_spi);
    
        memset(p_work, 0, sizeof(nrf_block_dev_spi_work_t));
        m_active_spi_dev = NULL;
        return NRF_SUCCESS;
    }
    
    static ret_code_t block_dev_spi_read_req(nrf_block_dev_t const * p_blk_dev,
                                              nrf_block_req_t const * p_blk)
    {
        ASSERT(p_blk_dev);
        ASSERT(p_blk);
        nrf_block_dev_spi_t const * p_spi_dev =
                                     CONTAINER_OF(p_blk_dev, nrf_block_dev_spi_t, block_dev);
        nrf_block_dev_spi_work_t *  p_work = p_spi_dev->p_work;
    
        ret_code_t ret = NRF_SUCCESS;
    
        NRF_LOG_INST_DEBUG(
            p_spi_dev->p_log,
            "Read req from block %"PRIu32" size %"PRIu32"(x%"PRIu32") to %"PRIXPTR,
            p_blk->blk_id,
            p_blk->blk_count,
            p_blk_dev->p_ops->geometry(p_blk_dev)->blk_size,
            p_blk->p_buff);
    
        if ((p_blk->blk_id + p_blk->blk_count) > p_work->geometry.blk_count)
        {
           NRF_LOG_INST_ERROR(
               p_spi_dev->p_log,
               "Out of range read req block %"PRIu32" count %"PRIu32" while max is %"PRIu32,
               p_blk->blk_id,
               p_blk->blk_count,
               p_blk_dev->p_ops->geometry(p_blk_dev)->blk_count);
           return NRF_ERROR_INVALID_ADDR;
        }
    
        if (m_active_spi_dev != p_spi_dev)
        {
            /* SPI instance is BUSY*/
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "Cannot read because SPI is busy");
            return NRF_ERROR_BUSY;
        }
    
        if (p_work->state != NRF_BLOCK_DEV_SPI_STATE_IDLE)
        {
            /* Previous asynchronous operation in progress*/
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "Cannot read because of ongoing previous operation");
            return NRF_ERROR_BUSY;
        }
    
        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_SPI_STATE_READ_EXEC;
        ret = nrf_drv_spi_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);
    
        if (ret != NRF_SUCCESS)
        {
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "SPI read error: %"PRIu32"", ret);
            p_work->state = NRF_BLOCK_DEV_SPI_STATE_IDLE;
            return ret;
        }
    
        p_blk_left->p_buff = NULL;
        p_blk_left->blk_count = 0;
    
        if (!p_work->ev_handler && (p_work->state != NRF_BLOCK_DEV_SPI_STATE_IDLE))
        {
            /*Synchronous operation*/
            wait_for_idle(p_spi_dev);
        }
    
        return ret;
    }
    
    static bool block_dev_spi_update_eunit(nrf_block_dev_spi_t const * p_spi_dev,
                                            size_t off,
                                            const void * p_src,
                                            size_t len)
    {
        ASSERT((len % sizeof(uint32_t)) == 0)
        nrf_block_dev_spi_work_t *  p_work = p_spi_dev->p_work;
    
        uint32_t *       p_dst32 = (uint32_t *)(p_work->p_erase_unit_buff + off);
        const uint32_t * p_src32 = p_src;
    
        bool erase_required = false;
        len /= sizeof(uint32_t);
    
        /*Do normal copying until erase unit is not required*/
        do
        {
            if (*p_dst32 != *p_src32)
            {
                if (*p_dst32 != BD_ERASE_UNIT_ERASE_VAL)
                {
                    erase_required = true;
                }
    
                /*Mark block as dirty*/
                p_work->erase_unit_dirty_blocks |= 1u << (off / p_work->geometry.blk_size);
            }
    
            *p_dst32++ = *p_src32++;
            off += sizeof(uint32_t);
        } while (--len);
    
        return erase_required;
    }
    
    static ret_code_t block_dev_spi_write_start(nrf_block_dev_spi_t const * p_spi_dev)
    {
        nrf_block_dev_spi_work_t *  p_work = p_spi_dev->p_work;
    
        if (!p_work->erase_required)
        {
            /*Get first block to program from program mask*/
            uint32_t block_to_program = __CLZ(__RBIT(p_work->erase_unit_dirty_blocks));
            uint32_t dst_address = (p_work->erase_unit_idx * NRF_BLOCK_DEV_SPI_ERASE_UNIT_SIZE) +
                                   (block_to_program * p_work->geometry.blk_size);
    
            const void * p_src_address = p_work->p_erase_unit_buff +
                                         block_to_program * p_work->geometry.blk_size;
    
            p_work->state = NRF_BLOCK_DEV_SPI_STATE_WRITE_EXEC;
            return nrf_drv_spi_write(p_src_address,
                                      p_work->geometry.blk_size,
                                      dst_address);
        }
    
        /*Erase is required*/
        uint32_t address = (p_work->erase_unit_idx * NRF_BLOCK_DEV_SPI_ERASE_UNIT_SIZE);
        p_work->state = NRF_BLOCK_DEV_SPI_STATE_WRITE_ERASE;
        p_work->erase_required = false;
    
     //..   return nrf_drv_spi_erase(NRF_SPI_ERASE_LEN_4KB, address);
    }
    
    static ret_code_t block_dev_spi_eunit_write(nrf_block_dev_spi_t const * p_spi_dev,
                                                 nrf_block_req_t * p_blk_left)
    {
        nrf_block_dev_spi_work_t *  p_work = p_spi_dev->p_work;
    
        size_t blk = p_blk_left->blk_id %
                     BD_BLOCKS_PER_ERASEUNIT(p_work->geometry.blk_size);
        size_t cnt = BD_BLOCKS_PER_ERASEUNIT(p_work->geometry.blk_size) - blk;
        size_t off = p_work->geometry.blk_size * blk;
    
        if (cnt > p_blk_left->blk_count)
        {
            cnt = p_blk_left->blk_count;
        }
    
        bool erase_required = block_dev_spi_update_eunit(p_spi_dev,
                                                          off,
                                                          p_blk_left->p_buff,
                                                          cnt * p_work->geometry.blk_size);
        if (erase_required)
        {
            p_work->erase_required = true;
        }
    
        p_blk_left->blk_count -= cnt;
        p_blk_left->blk_id += cnt;
        p_blk_left->p_buff = (uint8_t *)p_blk_left->p_buff + cnt * p_work->geometry.blk_size;
    
        if (p_work->erase_required)
        {
            uint32_t blk_size = p_work->geometry.blk_size;
            p_work->erase_unit_dirty_blocks |= (1u << BD_BLOCKS_PER_ERASEUNIT(blk_size)) - 1;
        }
    
        if (p_work->erase_unit_dirty_blocks == 0 || p_work->writeback_mode)
        {
            /*No dirty blocks detected. Write end.*/
            if (p_work->ev_handler && p_blk_left->blk_count == 0)
            {
                const nrf_block_dev_event_t ev = {
                        NRF_BLOCK_DEV_EVT_BLK_WRITE_DONE,
                        NRF_BLOCK_DEV_RESULT_SUCCESS,
                        &p_work->req,
                        p_work->p_context
                };
    
    
                p_work->state = NRF_BLOCK_DEV_SPI_STATE_IDLE;
                p_work->ev_handler(&p_spi_dev->block_dev, &ev);
                return NRF_SUCCESS;
            }
        }
    
        return block_dev_spi_write_start(p_spi_dev);
    }
    
    static ret_code_t block_dev_spi_write_req(nrf_block_dev_t const * p_blk_dev,
                                               nrf_block_req_t const * p_blk)
    {
        ASSERT(p_blk_dev);
        ASSERT(p_blk);
        nrf_block_dev_spi_t const * p_spi_dev =
                                     CONTAINER_OF(p_blk_dev, nrf_block_dev_spi_t, block_dev);
        nrf_block_dev_spi_work_t *  p_work = p_spi_dev->p_work;
    
        ret_code_t ret = NRF_SUCCESS;
    
        NRF_LOG_INST_DEBUG(
            p_spi_dev->p_log,
            "Write req to block %"PRIu32" size %"PRIu32"(x%"PRIu32") from %"PRIXPTR,
            p_blk->blk_id,
            p_blk->blk_count,
            p_blk_dev->p_ops->geometry(p_blk_dev)->blk_size,
            p_blk->p_buff);
    
        if ((p_blk->blk_id + p_blk->blk_count) > p_work->geometry.blk_count)
        {
           NRF_LOG_INST_ERROR(
               p_spi_dev->p_log,
               "Out of range write req block %"PRIu32" count %"PRIu32" while max is %"PRIu32,
               p_blk->blk_id,
               p_blk->blk_count,
               p_blk_dev->p_ops->geometry(p_blk_dev)->blk_count);
           return NRF_ERROR_INVALID_ADDR;
        }
    
        if (m_active_spi_dev != p_spi_dev)
        {
            /* SPI instance is BUSY*/
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "Cannot write because SPI is busy");
            return NRF_ERROR_BUSY;
        }
    
        if (p_work->state != NRF_BLOCK_DEV_SPI_STATE_IDLE)
        {
            /* Previous asynchronous operation in progress*/
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "Cannot write because of ongoing previous operation");
            return NRF_ERROR_BUSY;
        }
    
        p_work->left_req = *p_blk;
        p_work->req = *p_blk;
    
        nrf_block_req_t * p_blk_left = &p_work->left_req;
    
        uint32_t erase_unit = BD_BLOCK_TO_ERASEUNIT(p_blk_left->blk_id,
                                                    p_work->geometry.blk_size);
    
        /* Check if block is in erase unit buffer*/
        if (erase_unit == p_work->erase_unit_idx)
        {
            ret = block_dev_spi_eunit_write(p_spi_dev, p_blk_left);
        }
        else
        {
            if (p_work->writeback_mode)
            {
                ret = block_dev_spi_write_start(p_spi_dev);
            }
            else
            {
                p_work->erase_unit_idx = erase_unit;
                p_work->state = NRF_BLOCK_DEV_SPI_STATE_EUNIT_LOAD;
    
                ret = nrf_drv_spi_read(p_work->p_erase_unit_buff,
                        NRF_BLOCK_DEV_SPI_ERASE_UNIT_SIZE,
                        erase_unit * NRF_BLOCK_DEV_SPI_ERASE_UNIT_SIZE);
            }
        }
    
        if (ret != NRF_SUCCESS)
        {
            NRF_LOG_INST_ERROR(p_spi_dev->p_log, "SPI write error: %"PRIu32"", ret);
            p_work->state = NRF_BLOCK_DEV_SPI_STATE_IDLE;
            return ret;
        }
    
        if (!p_work->ev_handler && (p_work->state != NRF_BLOCK_DEV_SPI_STATE_IDLE))
        {
            /*Synchronous operation*/
            wait_for_idle(p_spi_dev);
        }
    
        return ret;
    }
    
    static ret_code_t block_dev_spi_ioctl(nrf_block_dev_t const * p_blk_dev,
                                           nrf_block_dev_ioctl_req_t req,
                                           void * p_data)
    {
        ASSERT(p_blk_dev);
        nrf_block_dev_spi_t const * p_spi_dev =
                                     CONTAINER_OF(p_blk_dev, nrf_block_dev_spi_t, block_dev);
        nrf_block_dev_spi_work_t *  p_work = p_spi_dev->p_work;
    
        switch (req)
        {
            case NRF_BLOCK_DEV_IOCTL_REQ_CACHE_FLUSH:
            {
                bool * p_flushing = p_data;
                NRF_LOG_INST_DEBUG(p_spi_dev->p_log, "IOCtl: Cache flush");
                if (p_work->state != NRF_BLOCK_DEV_SPI_STATE_IDLE)
                {
                    return NRF_ERROR_BUSY;
                }
    
                if (!p_work->writeback_mode || p_work->erase_unit_dirty_blocks == 0)
                {
                    if (p_flushing)
                    {
                        *p_flushing = false;
                    }
    
                    return NRF_SUCCESS;
                }
    
                ret_code_t ret =  block_dev_spi_write_start(p_spi_dev);
                if (ret == NRF_SUCCESS)
                {
                    if (p_flushing)
                    {
                        *p_flushing = true;
                    }
                    p_work->cache_flushing = true;
                }
    
                return ret;
            }
            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_spi_dev->info_strings;
                return NRF_SUCCESS;
            }
            default:
                break;
        }
    
        return NRF_ERROR_NOT_SUPPORTED;
    }
    
    static nrf_block_dev_geometry_t const * block_dev_spi_geometry(nrf_block_dev_t const * p_blk_dev)
    {
        ASSERT(p_blk_dev);
        nrf_block_dev_spi_t const * p_spi_dev =
                                     CONTAINER_OF(p_blk_dev, nrf_block_dev_spi_t, block_dev);
        nrf_block_dev_spi_work_t const * p_work = p_spi_dev->p_work;
    
        return &p_work->geometry;
    }
    
    const nrf_block_dev_ops_t nrf_block_device_spi_ops = {
            .init = block_dev_spi_init,
            .uninit = block_dev_spi_uninit,
            .read_req = block_dev_spi_read_req,
            .write_req = block_dev_spi_write_req,
            .ioctl = block_dev_spi_ioctl,
            .geometry = block_dev_spi_geometry,
    };
    
    
    volatile  uint8_t   SPIReadLength1, SPIWriteLength1; // variables to hold read and write lengths
    
    nrfx_err_t nrf_drv_spi_read(void *   p_rx_buffer,
                              size_t   rx_buffer_length,
                              uint32_t src_address)
    {
    
       spi_tx_buf[0] = (src_address);
       spi_xfer_done = false;
    
       APP_ERROR_CHECK(nrf_drv_spi_transfer(&m_spi,spi_tx_buf,2,p_rx_buffer,2));
    
       while(spi_xfer_done == false){};
    
       return spi_rx_buf[1];
    
    }
    
    
    
    /* A function to write into LIS3DSH Internal Register */
    nrfx_err_t nrf_drv_spi_write(void const * p_tx_buffer,size_t tx_buffer_length,uint32_t dst_address)
    {
        SPIWriteLength1 = 2; // set the spi write length to 2 bytes
        SPIReadLength1 = 0; // set the read length
        
        spi_tx_buf[0] = SET_WRITE_SINGLE_CMD(dst_address); // set the first byte which is a write command
        spi_tx_buf[1] = p_tx_buffer; // A byte of data to be sent
    	
        /* set the transfer flag to false to indicate data transfer has not yet started */
        spi_xfer_done = false;
        
        /* transfer the data by calling the spi transfer function and call it in app error check so if
           any error occurs it will catch it and sends it on debug port */
        APP_ERROR_CHECK(nrf_drv_spi_transfer(&m_spi, spi_tx_buf, tx_buffer_length, spi_rx_buf, SPIReadLength1));
    
        /* wait until the transfer is completed */
        while(!spi_xfer_done); // this flag will be set to true in the spi interrupt handler function
    }
    
    
    /** @} */
    #endif // NRF_MODULE_ENABLED(NRF_BLOCK_DEV_SPI)
    
    #include <stdint.h>
    #include <stdbool.h>
    #include <stddef.h>
    #include <inttypes.h>
    #include <stdlib.h>
    
    #include "nrf.h"
    #include "nrf_block_dev.h"
    #include "nrf_block_dev_ram.h"
    #include "nrf_block_dev_empty.h"
    #include "nrf_block_dev_qspi.h"
    #include "nrf_block_dev_spi.h"
    #include "nrf_block_dev_sdc.h"
    #include "nrf_drv_usbd.h"
    #include "nrf_drv_clock.h"
    #include "nrf_gpio.h"
    #include "nrf_atomic.h"
    #include "nrf_drv_power.h"
    #include "nrfx_spim.h"
    
    #include "ff.h"
    #include "diskio_blkdev.h"
    #include "usb_msc.h"
    
    #include "app_usbd.h"
    #include "app_usbd_core.h"
    #include "app_usbd_string_desc.h"
    #include "app_usbd_msc.h"
    #include "app_error.h"
    #include "app_timer.h"
    
    #include "bsp.h"
    
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    
    bool g_test_open = false;
    uint32_t g_idle_s = 0;
    
    BYTE rx_buff[1024];
    uint16_t rx_index;
    
    #define USE_FATFS_SPI    1
    
    static void msc_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                    app_usbd_msc_user_event_t     event);
    
    
    #ifndef USBD_POWER_DETECTION
    #define USBD_POWER_DETECTION true
    #endif
    
    /**
     * @brief SD card enable/disable
     */
    #define USE_SD_CARD       1
    
    
    #define FR_FILE_OPENED 1
    #define FR_FILE_CLOSED 2
    
    const char my_filename[] = "test1.xls";
    
    
    #if USE_SD_CARD
    
    #define SDC_SCK_PIN     (25)        //(27)        ///< SDC serial clock (SCK) pin.
    #define SDC_MOSI_PIN    (23)        //(26)        ///< SDC serial data in (DI) pin.
    #define SDC_MISO_PIN    (24)        //(2)         ///< SDC serial data out (DO) pin.
    #define SDC_CS_PIN      (15)        //(32 + 15)   ///< SDC chip select (CS) pin.
    
    #endif
    
    
    //NRF_BLOCK_DEV_SPI_DEFINE(
    //    m_block_dev_spi,
    //    NRF_BLOCK_DEV_SPI_CONFIG(
    //        512,
    //        NRF_BLOCK_DEV_SPI_FLAG_CACHE_WRITEBACK,
    //        NRF_DRV_SPI_DEFAULT_CONFIG
    //     ),
    //     NFR_BLOCK_DEV_INFO_CONFIG("Nordic", "SPI", "1.00")
    //);
    
    NRF_BLOCK_DEV_SPI_DEFINE(
        m_block_dev_spi,
        NRF_BLOCK_DEV_SPI_CONFIG(
            512,
            NRF_BLOCK_DEV_SPI_FLAG_CACHE_WRITEBACK,
            NRF_DRV_SPI_DEFAULT_CONFIG
         ),
         NFR_BLOCK_DEV_INFO_CONFIG("Nordic", "SPI", "1.00")
    );
    
    #define BLOCKDEV_LIST() (                                   \
        NRF_BLOCKDEV_BASE_ADDR(m_block_dev_spi, block_dev)     \
    )
    
    /**
     * @brief Endpoint list passed to @ref APP_USBD_MSC_GLOBAL_DEF
     */
    #define ENDPOINT_LIST() APP_USBD_MSC_ENDPOINT_LIST(1, 1)
    
    /**
     * @brief Mass storage class work buffer size
     */
    #define MSC_WORKBUFFER_SIZE (1024)
    
    /**
     * @brief Mass storage class instance
     */
    APP_USBD_MSC_GLOBAL_DEF(m_app_msc,
                            0,
                            msc_user_ev_handler,
                            ENDPOINT_LIST(),
                            BLOCKDEV_LIST(),
                            MSC_WORKBUFFER_SIZE);
    
    
    /**
     * @brief Events from keys
     */
    static nrf_atomic_u32_t m_key_events;
    
    /**
     * @brief  USB connection status
     */
    static bool m_usb_connected = false;
    
    
    #if USE_FATFS_SPI
    
    static FATFS m_filesystem;
    
    
    void fatfs_mkfs(void)
    {
        FRESULT ff_result;
    
        if (m_usb_connected)
        {
            NRF_LOG_ERROR("Unable to operate on filesystem while USB is connected");
            return;
        }
    
        NRF_LOG_INFO("\r\nCreating filesystem...");
        static uint8_t buf[512];
        ff_result = f_mkfs("", FM_FAT, 1024, buf, sizeof(buf));
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("Mkfs failed.");
            return;
        }
    
        NRF_LOG_INFO("Mounting volume...");
        ff_result = f_mount(&m_filesystem, "", 1);
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("Mount failed.");
            return;
        }
    
        NRF_LOG_INFO("Done");
    }
    
    
    
    
    static bool fatfs_init(void)
    {
        FRESULT ff_result;
        DSTATUS disk_state = STA_NOINIT;
    
        memset(&m_filesystem, 0, sizeof(FATFS));
    
        // Initialize FATFS disk I/O interface by providing the block device.
        static diskio_blkdev_t drives[] =
        {
            DISKIO_BLOCKDEV_CONFIG(NRF_BLOCKDEV_BASE_ADDR(m_block_dev_spi, block_dev), NULL)
        };
    
        diskio_blockdev_register(drives, ARRAY_SIZE(drives));
    
        NRF_LOG_INFO("Initializing disk 0 (SPI)...");
        disk_state = disk_initialize(0);
        if (disk_state)
        {
            NRF_LOG_ERROR("Disk initialization failed.disk_state<%d>", disk_state);
            return false;
        }
        NRF_LOG_ERROR("disk_state <%d>", disk_state);
        NRF_LOG_INFO("Mounting volume...");
        ff_result = f_mount(&m_filesystem, "", 1);
        if (ff_result != FR_OK)
        {
            if (ff_result == FR_NO_FILESYSTEM)
            {
                NRF_LOG_ERROR("Mount failed. Filesystem not found. Please format device.");
                fatfs_mkfs();// flash is new, should execute this here once, and then it can be annotated 
            }
            else
            {
                NRF_LOG_ERROR("Mount failed: %u", ff_result);
            }
            return false;
        }
    
        return true;
    }
    
    
    
    static void fatfs_ls(void)
    {
        DIR dir;
        FRESULT ff_result;
        FILINFO fno;
    
        if (m_usb_connected)
        {
            NRF_LOG_ERROR("Unable to operate on filesystem while USB is connected");
            return;
        }
    
        NRF_LOG_INFO("\r\nListing directory: /");
        ff_result = f_opendir(&dir, "/");
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("Directory listing failed: %u", ff_result);
            return;
        }
    
        uint32_t entries_count = 0;
        do
        {
            ff_result = f_readdir(&dir, &fno);
            if (ff_result != FR_OK)
            {
                NRF_LOG_ERROR("Directory read failed: %u", ff_result);
                return;
            }
    
            if (fno.fname[0])
            {
                if (fno.fattrib & AM_DIR)
                {
                    NRF_LOG_RAW_INFO("   <DIR>   %s\r\n",(uint32_t)fno.fname);
                }
                else
                {
                    NRF_LOG_RAW_INFO("%9lu  %s\r\n", fno.fsize, (uint32_t)fno.fname);
                }
            }
    
            ++entries_count;
            NRF_LOG_FLUSH();
        } while (fno.fname[0]);
    
    
        NRF_LOG_RAW_INFO("Entries count: %u\r\n", entries_count);
    }
    
    
    
    
    static void fatfs_file_create(void)
    {
        FRESULT ff_result;
        FIL file;
    
    
        if (m_usb_connected)
        {
            NRF_LOG_ERROR("Unable to operate on filesystem while USB is connected");
            return;
        }
    
        NRF_LOG_RAW_INFO("Creating random file: %s ...", (uint32_t)my_filename);
        NRF_LOG_FLUSH();
    
        ff_result = f_open(&file, my_filename, FA_CREATE_ALWAYS | FA_WRITE);
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("\r\nUnable to open or create file: %u", ff_result);
            NRF_LOG_FLUSH();
            return;
        }
    
    //write a string
    
        static BYTE test_string[] = "christophe\t1995\t0707\r\nsid\t1997\t1234\r\n";
        UINT data_len_written = 0;
        ff_result = f_write(&file, test_string, (UINT)strlen(test_string), &data_len_written);
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("\r\nUnable to write data: %u", ff_result);
            NRF_LOG_FLUSH();
            return;
        }
    
    
        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");
    
    }
    
    
    static FRESULT my_file_open(FIL* my_file)
    {
        FRESULT ff_result;
        ret_code_t err_code;
    
        if (g_test_open)
        {
            return FR_FILE_OPENED;
        }
    
        ff_result = f_open(my_file, my_filename, FA_OPEN_ALWAYS | FA_WRITE | FA_OPEN_APPEND);
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("\r\nUnable to open or create file: %u", ff_result);
            NRF_LOG_FLUSH();
            return ff_result;
        }
    
    #if CLOSE_FILE_BY_TIMER
        err_code = app_timer_start(file_close_timer_id, CLOSE_INTERVAL, NULL);
        APP_ERROR_CHECK(err_code);
    #endif
    
        g_test_open = true;
    
          return ff_result;
    }
    
    #if 0
    static void my_file_write(void)
    {
        FRESULT ff_result;
        FIL file;
    
        ff_result = f_write(&file, test, (UINT)strlen(test), &written);
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("\r\nUnable to write data: %u", ff_result);
            NRF_LOG_FLUSH();
            return;
        }
    
    }
    #endif
    
    static FRESULT my_file_close(FIL * my_file)
    {
        FRESULT ff_result;
    
        if (g_test_open == false)
        {
            return FR_FILE_CLOSED;
           
        }
    
        ff_result = f_close(my_file);
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("\r\nUnable to close file: %u", ff_result);
            NRF_LOG_FLUSH();
            return ff_result;
        }
        NRF_LOG_RAW_INFO("done\r\n");
    
        g_test_open = false;
    #if CLOSE_FILE_BY_TIMER
        //app_timer_stop(file_close_timer_id);
    #endif
        NRF_LOG_RAW_INFO("file closed!\r\n"); 
        NRF_LOG_FLUSH();
    
         return ff_result;
    
    }
    
    
    static void my_write_data(void)
    {
    
        FRESULT ff_result;
        FIL file;
    
        ff_result = f_open(&file, my_filename, FA_CREATE_ALWAYS | FA_WRITE);
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("\r\nUnable to open or create file: %u", ff_result);
            NRF_LOG_FLUSH();
            return;
        }
    
    //write a string
    
    #if 1
    //    ff_result = f_lseek(&file, file.obj->fs.fsize));
    //    if (ff_result != FR_OK)
    //    {
    //        NRF_LOG_ERROR("\r\nUnable to write data: %u", ff_result);
    //        NRF_LOG_FLUSH();
    //        return;
    //    }
    
    
        static BYTE test[] = "1111\t2222\t3333\r\n4444\t5555\t6666\r\n";
        UINT written = 0;
        ff_result = f_write(&file, test, (UINT)strlen(test), &written);
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("\r\nUnable to write data: %u", ff_result);
            NRF_LOG_FLUSH();
            return;
        }
    
        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");
    
    }
    
    static void fatfs_uninit(void)
    {
        NRF_LOG_INFO("Un-initializing disk 0 (SPI)...");
        UNUSED_RETURN_VALUE(disk_uninitialize(0));
    }
    #else //USE_FATFS_SPI
    #define fatfs_init()        false
    #define fatfs_mkfs()        do { } while (0)
    #define fatfs_ls()          do { } while (0)
    #define fatfs_file_create() do { } while (0)
    #define fatfs_uninit()      do { } while (0)
    #endif
    
    /**
     * @brief Class specific event handler.
     *
     * @param p_inst    Class instance.
     * @param event     Class specific event.
     */
    static void msc_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                    app_usbd_msc_user_event_t     event)
    {
        UNUSED_PARAMETER(p_inst);
        UNUSED_PARAMETER(event);
    }
    
    
    /**
     * @brief USBD library specific event handler.
     *
     * @param event     USBD library event.
     */
    static void usbd_user_ev_handler(app_usbd_event_type_t event)
    {
        switch (event)
        {
            case APP_USBD_EVT_DRV_SUSPEND:
                //bsp_board_led_off(LED_USB_RESUME);
                break;
            case APP_USBD_EVT_DRV_RESUME:
                //bsp_board_led_on(LED_USB_RESUME);
                break;
            case APP_USBD_EVT_STARTED:
                //bsp_board_led_on(LED_USB_START);
                break;
            case APP_USBD_EVT_STOPPED:
                UNUSED_RETURN_VALUE(fatfs_init());
                app_usbd_disable();
                //bsp_board_leds_off();
                break;
            case APP_USBD_EVT_POWER_DETECTED:
                NRF_LOG_INFO("USB power detected");
    
                if (!nrf_drv_usbd_is_enabled())
                {
                    fatfs_uninit();
                    app_usbd_enable();
                }
                break;
            case APP_USBD_EVT_POWER_REMOVED:
                NRF_LOG_INFO("USB power removed");
                app_usbd_stop();
                m_usb_connected = false;
                //memset(&m_rx, 0, sizeof(m_rx));
                break;
            case APP_USBD_EVT_POWER_READY:
                NRF_LOG_INFO("USB ready");
                app_usbd_start();
                m_usb_connected = true;
                break;
            default:
                break;
        }
    }
    
    
    #endif
    
    
    static void close_file_handler(void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        g_idle_s++;
    
    }
    
    
    
    void usbd_msc_task(void)
    {
        FRESULT ff_result;
        FIL file;
    
       
        spi_init();
       //$$ nrf_gpio_pin_clear(SPI_SS_FLASH);
       // nrf_gpio_pin_set(SPI_SS_USB);
    
        ret_code_t ret;
     
    
        if (fatfs_init())
            {
                  fatfs_ls();
                  fatfs_file_create();
            }
    
        //ret = app_usbd_init(&usbd_config);
        //APP_ERROR_CHECK(ret);
    
        //app_usbd_class_inst_t const * class_inst_msc = app_usbd_msc_class_inst_get(&m_app_msc);
        //ret = app_usbd_class_append(class_inst_msc);
        //APP_ERROR_CHECK(ret);
    
        NRF_LOG_INFO("USBD MSC example started.");
        
        if (USBD_POWER_DETECTION)
        {
            ret = app_usbd_power_events_enable();
            APP_ERROR_CHECK(ret);
        }
        else
        {
            NRF_LOG_INFO("No USB power detection enabled\r\nStarting USB now");
    
            app_usbd_enable();
            app_usbd_start();
            m_usb_connected = true;
        }
    
        uint8_t write_cnt = 0;
    
    
       // memset(&m_rx, 0, sizeof(m_rx));
        NRF_LOG_INFO("New Flash to write.");
    
    #if 1 //read JEDEC ID
    
        nrf_qspi_cinstr_conf_t cinstr_cfg = {
            .opcode    = 0x9F,
            .length    = 4,
            .io2_level = true,
            .io3_level = true,
            .wipwait   = true,
            .wren      = true
        };
    
        //uint8_t rdid_buf[3] = {0, 0, 0};
        //ret = nrfx_spim_xfer(&cinstr_cfg, NULL, rdid_buf);
        //if (ret != NRF_SUCCESS)
        //{
        //    NRF_LOG_INFO("read eror.");
        //    //NRF_LOG_INST_ERROR(p_qspi_dev->p_log, "QSPI get 3 byte id error: %"PRIu32"", ret);
        //    //return ret;
        //}
        //NRF_LOG_INFO("rdid_buf [%02x %02x %02x]", rdid_buf[0], rdid_buf[1], rdid_buf[2]);
    #endif
    
                fatfs_mkfs();
    
                fatfs_file_create();
                fatfs_file_create();
                fatfs_file_create();
            
                fatfs_ls();
    
        while (true)
        {
    
            while (app_usbd_event_queue_process())
            {
                /* Nothing to do */
            }
           
          NRF_LOG_FLUSH();
        }
    
    }
    
    
    void usbd_init(void)
    {
        ret_code_t ret;
    
        static const app_usbd_config_t usbd_config = {
            .ev_state_proc = usbd_user_ev_handler
        };
    
        ret = app_usbd_init(&usbd_config);
        APP_ERROR_CHECK(ret);
    
        app_usbd_class_inst_t const * class_inst_msc = app_usbd_msc_class_inst_get(&m_app_msc);
        ret = app_usbd_class_append(class_inst_msc);
        APP_ERROR_CHECK(ret);
    
    }

    I doubt my configuration is wrong. 

    The error shows "Initializing disk 0 (SPI)...
    <error> app: SPI init error: 8
    <error> app: Disk initialization failed.disk_state<1>"

    Init error 8 states Invalid state

    Thanks & Regards,

    Snehal

  • Hi Sigurd, I have managed to connect to the SPI now. I do not see any error for Disk initialization. But while doing f_mount, the code is getting stuck in the function "check_fs". I am not sure why this is happening. Can you please help?? 

    I am getting softdevice assertion failed.

    The code is actually getting stuck in function nrf_drv_spi_read. I guess my implementation is wrong for spi. Can you please tell me how to implement "nrfx_qspi_read" and the functions inside it to work with SPI drivers???

    Thanks & Regards,

    Snehal 

Reply
  • Hi Sigurd, I have managed to connect to the SPI now. I do not see any error for Disk initialization. But while doing f_mount, the code is getting stuck in the function "check_fs". I am not sure why this is happening. Can you please help?? 

    I am getting softdevice assertion failed.

    The code is actually getting stuck in function nrf_drv_spi_read. I guess my implementation is wrong for spi. Can you please tell me how to implement "nrfx_qspi_read" and the functions inside it to work with SPI drivers???

    Thanks & Regards,

    Snehal 

Children
Related