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

Parents
  • Dear Gianluca,

    Thank you for your patience.

    As you want to have two spi devices (SDCard and another peripheral): you can put both on the same spi controller by having different CS pins, 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.

    If you (and your hardware) have the flexibility, then it is preferred to use separate instance for SDCard as sharing another spi peripheral with the SDCard might not be very good idea.

    Nonetheless, as in your case, you have a SDCard and another peripheral that you are putting on the same spi-bus and using spi0 controller. 

    Also, as you are using FATFS sample, you have seen that this sample initializes spi internally and you don't have to do any initialization like you are doing for your other peripheral.

    What I mean is that: for the other peripheral, you have written spi_master_init() function but we have not written any such initialization function for the SDCard as we know that the FATFS must be handling internally.

    Saying that, the problem you are facing seems to be due to the fact that the instance is already initialized, and you are trying to reinitialize it. As per your code:

    err_code = spi_master_init();
    CDCInit();                    
    nrf_delay_ms(1000);
    
    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();
    fatfs_example();                 

    If you are calling fatfs example first, then do something like this:

    fatfs_example();
    
    nrf_drv_spi_uninit(&spi);
    
    spi_transceiver_init();    
    
    while (1) 
        {   
            // ...
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_buf, m_length, m_rx_buf, m_length));
            while (!spi_xfer_done)
            {
              __WFE();
            }
            // ...
            nrf_delay_ms(1000);
        }

    where, after fatfs example, we uninitialize the spi instance driver, and then re-initializ (in this spi_transceiver_init() function like spi_master_init() ) before using it.

    Best regards,

    Naeem

  • 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

Reply
  • 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

Children
  • 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?

  • I don't know for sure whether I have exact answer for your query or not,

    but we can see in the fatfs example that first static initializations are done and then there are two steps namely disk-initialization and disk-mounting. After these steps you can read and write from the disk. Similarly, afterwards we need to unmount and then uninitialize the disk if we want to use the other peripheral. I can see that just going through fatfs_example function takes approximately 10ms.

Related