Sharing SPI interface between SD card and other peripheral

Dear DevZone,

I am trying to share a SPI instance between an SD card and an other peripheral. I am working with nRF52840. 

I already opened a ticket back in time in devzone.nordicsemi.com/.../sd-card-disk_initialization-fails-when-the-fatfs-example-is-integrated-in-my-fw-project for initial configuration of the fatfs example.

Now, the fatfs example is correctly initializing the board if no other SPI periherals are initialized.

However, if I try to initialize my other peripheral before the fatfs example, the other peripheral correctly initializes, but the fatfs example goes in BREAKPOINT condition when trying to initialize the disk (disk_initialization) during spi setup.  Here the SPI initialization from the other peripheral. I deassert the CS pin after the other peripheral initialization

// spi_master_init: function that initializes the SPI master, its pins and the order in which it sends the bits on the bus
uint32_t spi_master_init(void)
{
	uint32_t err_code;
        nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
        spi_config.ss_pin   = NRF_DRV_SPI_PIN_NOT_USED;                             // unconnected, we are going to use a custom function to switch between SSx
        spi_config.miso_pin = SPI_MISO_PIN;
        spi_config.mosi_pin = SPI_MOSI_PIN;
        spi_config.sck_pin  = SPI_SCK_PIN;
	spi_config.bit_order = SPI_CONFIG_ORDER_MsbFirst;
	
        err_code = nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL);
        APP_ERROR_CHECK(err_code);
}

The same (but specular) happens if I try to run before the fatfs example and then initialize the other peripheral: the fatfs correctly initializes the SD card, but then the SPI initialization fails with a BREAKPOINT condition when it tryies to do a transfer. Here the two sequences of operations:

int main(void)
{
    status = INIT;
    ret_code_t err_code;
    uint32_t time_ticks;

    init_message_data();

    bsp_board_init(BSP_INIT_LEDS);


    gpio_init();


//    // initialization of SPI protocol
    err_code = spi_master_init();

//******************AD7147 initialization*********************************

    // this call initializes the converters
    CDCInit();                    

//*******************************************************************************************





    // some delay needed to allow the previous operations to be successfull
    nrf_delay_ms(1000);
//******************SD card initialization*********************************
    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();
    // this call initializes the SD card
    fatfs_example();                  

//*******************************************************************************************

Here the breakpoint condition - NRF_LOG_ERROR("ERROR %u [%s] at %s:%u\r\nPC at: 0x%08x",:

        case NRF_FAULT_ID_SDK_ERROR:
        {
            error_info_t * p_info = (error_info_t *)info;
            NRF_LOG_ERROR("ERROR %u [%s] at %s:%u\r\nPC at: 0x%08x",
                          p_info->err_code,
                          nrf_strerror_get(p_info->err_code),
                          p_info->p_file_name,
                          p_info->line_num,
                          pc);
             NRF_LOG_ERROR("End of error report");
            break;
        }
        default:
            NRF_LOG_ERROR("UNKNOWN FAULT at 0x%08X", pc);
            break;
    }
#endif

    NRF_BREAKPOINT_COND;

Which can be the issue? 

Thank you very much,

best regards

Gianluca

  • Dear Naeem,

    thank you very much for your answer.

    I tried unitialize and reinitialize the fatfs between each usage of the peripherals, but it seems that the fatfs SPI initialization lasts too much time (around 10ms), which I cannot spend.

    Is it possible then in your opinion to initialize two SPI instances sharing the same CLK, MOSI, MISO pins? as you wrote here:

    or you can put one device on spi0 and other on spi1 (multiple spi instances) in which case we don't need to control through the CS pins.

    In that case I do not need reinitialization every time right? Is it feasible in your opinion even with the same pins? Do I need to make any particular action?

    Thanks again,

    best regards

    Gianluca

  • Hi Gianluca,

    No. Different spi instances (e.g. spi0 and spi1) need to be initialized with their own set of pins.

    As in this case, each will have a single device on their bus, they will not need CS pin to enable that.

    BR,
    Naeem

  • Thanks a lot Naeem.

    I asked this also because in this old DevZone post  Multiple SPI instances - share pins? , it is stated that the pin sharing between instances is possible. So do you confirm it is not, right?

    In the latter case, I would do what you suggested above, i.e. uninitialize and reinitialize the SPI instance every time I need to switch. I would ask you for a doubt:

     which is the minimal initalization procedure for the fatfs SPI ? I currently tried to calculate the time to run the fatfs example including the whole disk_initialize function, and it lasts 10ms. it is too much time for my application if I need to do it every time I switch. Do you think it would be possible to remove part of this function when I reinitialize the fatfs SPI instance?

    Here the fatfs_example function including the disk_initialize call:

    static void fatfs_example()
    {
        static FATFS fs;
        static DIR dir;
        static FILINFO fno;
        static FIL file;
    
     
    
        uint32_t bytes_written;
        FRESULT ff_result;
        DSTATUS disk_state = STA_NOINIT;
    
     
    
        // 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_sdc, block_dev), NULL)
        };
    
     
    
        diskio_blockdev_register(drives, ARRAY_SIZE(drives));
    
     
    
        NRF_LOG_INFO("Initializing disk 0 (SDC)...");
        SEGGER_RTT_printf(0,"Initializing disk 0 (SDC)...");
        for (uint32_t retries = 3; retries && disk_state; --retries)
        {
            disk_state = disk_initialize(0);
        }
        if (disk_state)
        {
            //NRF_LOG_INFO("Disk initialization failed.");
            SEGGER_RTT_printf(0,"Disk initialization failed.");
            return;
        }
    
     
    
        uint32_t blocks_per_mb = (1024uL * 1024uL) / m_block_dev_sdc.block_dev.p_ops->geometry(&m_block_dev_sdc.block_dev)->blk_size;
        uint32_t capacity = m_block_dev_sdc.block_dev.p_ops->geometry(&m_block_dev_sdc.block_dev)->blk_count / blocks_per_mb;
        //NRF_LOG_INFO("Capacity: %d MB", capacity);
        SEGGER_RTT_printf(0, "Capacity:  %d ", capacity);
    
     
    
        //NRF_LOG_INFO("Mounting volume...");
        SEGGER_RTT_printf(0,"Mounting volume...");
        ff_result = f_mount(&fs, "", 1);
        if (ff_result)
        {
            //NRF_LOG_INFO("Mount failed.");
            SEGGER_RTT_printf(0,"Mount failed.");
            return;
        }
    
     
    
        //NRF_LOG_INFO("\r\n Listing directory: /");
        SEGGER_RTT_printf(0,"\r\n Listing directory: /");
        ff_result = f_opendir(&dir, "/");
        if (ff_result)
        {
           // NRF_LOG_INFO("Directory listing failed!");
            SEGGER_RTT_printf(0,"Directory listing failed!");
            return;
        }
    
     
    
        do
        {
            ff_result = f_readdir(&dir, &fno);
            if (ff_result != FR_OK)
            {
                //NRF_LOG_INFO("Directory read failed.");
                SEGGER_RTT_printf(0,"Directory read failed.");
                return;
            }
    
     
    
            if (fno.fname[0])
            {
                if (fno.fattrib & AM_DIR)
                {
                    //NRF_LOG_RAW_INFO("   <DIR>   %s",(uint32_t)fno.fname);
                }
                else
                {
                    //NRF_LOG_RAW_INFO("%9lu  %s", fno.fsize, (uint32_t)fno.fname);
                }
            }
        }
        while (fno.fname[0]);
        //NRF_LOG_RAW_INFO("");
        //NRF_LOG_INFO("Writing to file " FILE_NAME "...");
        SEGGER_RTT_printf(0,"Writing to file " FILE_NAME "...");
    
     
    
        ff_result = f_open(&file, FILE_NAME, FA_READ | FA_WRITE | FA_OPEN_APPEND);
        if (ff_result != FR_OK)
        {
            //NRF_LOG_INFO("Unable to open or create file: " FILE_NAME ".");
            SEGGER_RTT_printf(0,"Unable to open or create file: " FILE_NAME ".");
            return;
        }
    
     
    
        ff_result = f_write(&file, TEST_STRING, sizeof(TEST_STRING) - 1, (UINT *) &bytes_written);
        if (ff_result != FR_OK)
        {
            //NRF_LOG_INFO("Write failed\r\n.");
            SEGGER_RTT_printf(0,"Write failed\r\n.");
        }
        else
        {
            //NRF_LOG_INFO("%d bytes written.", bytes_written);
            SEGGER_RTT_printf(0,"%d bytes written.", bytes_written);
        }
    
     
    
        (void) f_close(&file);
        return;
    }

    Here the body of the disk_initalize function:

    DSTATUS disk_initialize(BYTE drv)
    {
        ASSERT(m_drives);
    
        if (drv >= m_drives_count)
        {
            return (STA_NODISK | STA_NOINIT);
        }
    
        if (!m_drives[drv].config.p_block_device)
        {
            return (STA_NODISK | STA_NOINIT);
        }
    
        if (!(m_drives[drv].state & STA_NOINIT))
        {
            // Disk already initialized.
            return m_drives[drv].state;
        }
    
        if (m_drives[drv].config.wait_func == NULL)
        {
            m_drives[drv].config.wait_func = default_wait_func;
        }
    
        m_drives[drv].busy = true;
        ret_code_t err_code = nrf_blk_dev_init(m_drives[drv].config.p_block_device,
                                               block_dev_handler,
                                               (void *) (uint32_t) drv);
        if (err_code == NRF_SUCCESS)
        {
            while (m_drives[drv].busy)
            {
                m_drives[drv].config.wait_func();
            }
    
            if (m_drives[drv].last_result == NRF_BLOCK_DEV_RESULT_SUCCESS)
            {
                m_drives[drv].state &= ~STA_NOINIT;
            }
        }
    
        return m_drives[drv].state;
    }
    

    Thanks again,

    Best regards

    Gianluca

  • Hello again,

    Gianlucamilani said:
    it is stated that the pin sharing between instances is possible.

    It would be possibly but you will have to do more, like uninitialize and reinitialize.

    That is what that ticket has in its conclusion: 

    "Yeah, you're right. The SPIM0-2 uses SW Chip Select and you'll have to uninit and then re-init the driver with the new configuration (CS/SS pin and whatever else you want to change) whenever you want to address another slave"

    Gianlucamilani said:
    which is the minimal initalization procedure for the fatfs SPI

    I don't know what you mean here. 

    Gianlucamilani said:
    I currently tried to calculate the time to run the fatfs example including the whole disk_initialize function, and it lasts 10ms

    I guess that is internal to how disk drivers do the initialization. It might also depend on the size of the SD card.

    Can you try different card size and again check if it makes any difference.

    Also, number of files stored on the SDCard contribute to that time.

  • I guess that is internal to how disk drivers do the initialization. It might also depend on the size of the SD card.

    Can you try different card size and again check if it makes any difference.

    Also, number of files stored on the SDCard contribute to that time.

    Perfect, thanks Naeem

    I don't know what you mean here. 

    I mean: do you know the minimal set of commands/functions to reinitialize the SPI instance related to the SD?

Related