app_sdc_init() not allowed to use XL1 (P0.0) as sck pin? / Proper setup of SD Card with other SPI usage

NRF52840, S140, SDK 14.2

I am trying to write to an SD Card. But whenever I insert the card, I get a failure on ASSERT. I believe this is because I am using P0.0 as the SCK pin, and anything &-ed with 0 is 0. 

Does this mean I am not allowed to use the 0.0 SCK pin? That seems wrong. Was this fixed in a later SDK update? 

When I comment out the &&  p_config->sck_pin, I no longer get the Assert. however, I get this error instead (triggered from nrf_drv_spi_init()) 

which is NRF_ERROR_INVALID_STATE (Invalid state, operation disallowed in this state)

I suspect this is because now that I am trying to use the SD Card, there are 2 SPI resources on the system. I configured the SD Card as follows:

Whereas the other SPI device was configured "normally":

 (i do app_error_check in the fn that calls spi_config). 

This error happens and the application freezes immediately when I use SPI 0 for the second SPI device. When I use SPI 1 for the second SPI device without changing the GPIO pins, I get strange behavior: my main loop stops running entirely (seems to hit power manage and just stop? but I have interrupts coming in), and garbage data (instead of the data that is normally sent) goes out on my SPI line to the second device. I am able to still connect over BLE though. Why is this happening?

I read this and this. My other questions:

  1. What SPI instance is the SD Card using? SPI 0? 
  2. Is each SPI instance supposed to use its own separate GPIO?

If this is the case that I have to use a separate SPI instance with separate GPIO, it is a bit disappointing that NRF doesn't let you use SPI for its intended purpose (multiplexing data on a bus). I am a little low on board space and would prefer not to route 3 extra lines if possible. Please advise. What should my path forward be, to use SPI with SD card and a different device?

Thank you!

Parents
  • Hi,

    1. The SPI instance is configurable through the sdk_config.h define APP_SDCARD_SPI_INSTANCE.
    2. Yes, each SPI instance requires its own set of GPIOs, if multiple instances are enabled at the same time. The serial peripherals of the nRF52 ICs can be configured to use any GPIO, so it is possible to configure different SPI instances to use the same GPIOs, given that you first disable any other SPI instances configured to use these GPIOs.
    If this is the case that I have to use a separate SPI instance with separate GPIO, it is a bit disappointing that NRF doesn't let you use SPI for its intended purpose (multiplexing data on a bus).

    This is not a limitation of the SPI peripheral in the chip itself, it is just how the SD Card library is written. You are free to use the SPI peripheral for more than one slave by controlling the CSN/SS pin from your code. As I mention in the second thread you linked, it is possible to modify the library to provide the SPI instance from outside the library, to utilize it for other SPI devices as well. Another option is to uninitialize the app_sdcard library before initializing the SPI driver for other operations, and similarly uninitialize the SPI driver before re-enabling the app_sdcard library.

    Best regards,
    Jørgen

Reply
  • Hi,

    1. The SPI instance is configurable through the sdk_config.h define APP_SDCARD_SPI_INSTANCE.
    2. Yes, each SPI instance requires its own set of GPIOs, if multiple instances are enabled at the same time. The serial peripherals of the nRF52 ICs can be configured to use any GPIO, so it is possible to configure different SPI instances to use the same GPIOs, given that you first disable any other SPI instances configured to use these GPIOs.
    If this is the case that I have to use a separate SPI instance with separate GPIO, it is a bit disappointing that NRF doesn't let you use SPI for its intended purpose (multiplexing data on a bus).

    This is not a limitation of the SPI peripheral in the chip itself, it is just how the SD Card library is written. You are free to use the SPI peripheral for more than one slave by controlling the CSN/SS pin from your code. As I mention in the second thread you linked, it is possible to modify the library to provide the SPI instance from outside the library, to utilize it for other SPI devices as well. Another option is to uninitialize the app_sdcard library before initializing the SPI driver for other operations, and similarly uninitialize the SPI driver before re-enabling the app_sdcard library.

    Best regards,
    Jørgen

Children
  • Thank you Jorgen. I will have to think through these options.

    Can you also answer my first question about not being able to use P0.0 as the clock if you use the SD Card Library? Is this a known bug / is it fixed in later SDKs?

  • Sorry about jumping over that question.

    Yes, I agree that this is a bug, and P0.00 should be usable with SPI/SDC library. I have reported this bug internally.

  • Thanks. I decided to turn off / get rid of SPI communication to the extra device while I am working on the SD card for now, and may ultimately use a separate SPI instance for both. 

    ISSUE RUNNING NRF SDC LIBRARY EXAMPLE

    I am running into a separate problem now, which is that I can't seem to make the SD Card example work. I am following the instructions here: https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v14.2.0/app_sdcard_example.html

    There is a typo, it says this is the SAADC example, but I assume the instructions are still correct? 

    I have a custom board and have changed my GPIO pins accordingly (as below... and I changed nothing else but these pins):

    #define FILE_NAME   "NORDIC.TXT"
    #define TEST_STRING "SD card example."
    
    #define SDC_SCK_PIN     NRF_GPIO_PIN_MAP(0, 00) // ARDUINO_13_PIN  ///< SDC serial clock (SCK) pin.
    #define SDC_MOSI_PIN    NRF_GPIO_PIN_MAP(1, 12) // ARDUINO_11_PIN  ///< SDC serial data in (DI) pin.
    #define SDC_MISO_PIN    NRF_GPIO_PIN_MAP(0, 30) // ARDUINO_12_PIN  ///< SDC serial data out (DO) pin.
    #define SDC_CS_PIN      NRF_GPIO_PIN_MAP(0, 26) // ARDUINO_10_PIN  ///< SDC chip select (CS) pin.

    However, no data gets written to the SD Card. Also, I notice that pins 17 and 15 have gone high, as they are attached to LEDs, which are now on, and I did not specify that this should occur anywhere in the code. Do you know what might be wrong?

    --------------

    ISSUE RUNNING CUSTOM SD CARD CODE

    I HAVE however, gotten the SD Card to be recognized on my custom board using custom code that another engineer seems to have based off of the example. I am using a ExFat formatted card. But my problems are:

    1. Before I enabled ExFat inside ff.h, my SDC was getting initialized fine, but where it kept getting stuck is the file system does not get initialized properly. f_mount() keeps failing with FR_NO_FILESYSTEM. 

    2. After I enabled ExFat inside ff.h, my SDC is not even being initialized. See code below; the error I keep getting is " Disk initialization failed." (line 22).  Why might this be the case? I read here that low voltage could keep the disk from mounting, but does it also prevent initialization? Why would the SDC have been successfully initialized before enabling ExFat and not after?

    Code here:

    static void insert_sd_logic() {
    	NRF_LOG_DEBUG("insert_sd_logic");
    	sdc_card_inserted = SDC_CARD_INSERTED;
    	DSTATUS disk_state = STA_NOINIT;
    	static FATFS fs;
    	// 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_DEBUG("Initializing disk 0 (SDC)...");
    	for (uint32_t retries = 3; retries && disk_state; --retries)
    	{
    		disk_state = disk_initialize(0);
    		NRF_LOG_INFO("disk_state %x", disk_state);
    	}
    	if (disk_state)
    	{
    		NRF_LOG_ERROR("Disk initialization failed.");
    		sdc_status = SDC_STATUS_ERROR;
    		sdc_msg = SDC_MSG_ERROR_INIT;
    		update_sdc_status(sdc_card_inserted, sdc_status, sdc_msg);
    		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);
    
    	NRF_LOG_DEBUG("Mounting volume...");
    	card_mounted = f_mount(&fs, "", 1);
    	NRF_LOG_INFO("card_mounted %d", card_mounted);
    	if (card_mounted != FR_OK)
    	{
    		NRF_LOG_ERROR("Mount failed.");
    		sdc_status = SDC_STATUS_ERROR;
    		sdc_msg = SDC_MSG_ERROR_MOUNT;
    		update_sdc_status(sdc_card_inserted, sdc_status, sdc_msg);
    		return;
    	}
    }

    I'm not sure if this is important, by why is ENTER_FF() not considered a part of the code, below? (see question mark in breakpoint)

Related