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

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

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

  • The issue has not happened for a few days now. Not sure what the issue was. But the SD card is working great. You can probably consider this closed. Thank you again for your help. 

  • Thanks a lot for the reply! Your notes are very helpful. I was able to get data written with my custom board, but for reasons you mentioned, the example does not work with my custom board very well.

    Are you sure that it was this change that caused it to no longer initialize?

    Turns out that it does actually sometimes (and mostly all the time now) initialize properly. But it seems like sometimes the card does not get mounted properly if it gets pushed in too fast? Does that make sense to you? The problem has become sporadic and seems loosely correlated with how gently I push in the SD card to the connector.

    By the way, is there any way to detect whether the SD Card is full? I know how to read the capacity, but I wasn't sure if that is total capacity (regardless of how full) or capacity remaining. 

    Thanks!

  • Are you using some card detect signal from the socket to detect the card? Do you immediately initialize the card when detecting the signal?

    You may try introducing a short delay, to allow the card to fully insert, or to avoid signal bounce issues. You can use app_timer for this purpose. Start the timer on the first reception of the "card detected" signal, check that the signal is still in the "card detected" state at timeout, and then initialize the card.

  • Thank you, Jorgen.

    I do use a card detect signal (card detect pin goes low when card is inserted), and I have a 1s timer between insertion detection and initialization. i want to say there were more issues before I put the timer in, but there are still issues sometimes. I have built in an LED warning light to let the user know they should remove and re-insert if it fails to initialize or mount. 

    1) Can you answer this question too?

    By the way, is there any way to detect whether the SD Card is full? I know how to read the capacity, but I wasn't sure if that is total capacity (regardless of how full) or capacity remaining.

    2) There seems to be a bug in the SDC part of the SDK: 

    The code ASSERTS if sdc_cmd returns NRF_ERROR_BUSY. Adding line 10 below (which is the condition that causes sdc_cmd to return this error) fixes the Assert.

    ret_code_t app_sdc_block_read(uint8_t * p_buf, uint32_t block_address, uint16_t block_count)
    {
        ASSERT(p_buf);
    
        if (m_cb.state.op == SDC_UNINITIALIZED)
        {
            return NRF_ERROR_INVALID_STATE;
        }
        if (m_cb.state.op != SDC_OP_IDLE
    			|| m_cb.state.bus_state != SDC_BUS_IDLE)	// WARNING: SDK mod
        {
            return NRF_ERROR_BUSY;
        }
        if (block_count == 0)
        {
            return NRF_ERROR_INVALID_PARAM;
        }
    
        m_cb.state.op = SDC_OP_READ;
    
        if (!m_cb.info.type.sdhc)
        {
            m_cb.state.rw_op.address = block_address * SDC_SECTOR_SIZE;
        }
        else
        {
            m_cb.state.rw_op.address = block_address;
        }
        m_cb.state.rw_op.buffer = p_buf;
        m_cb.state.rw_op.block_count = block_count;
        m_cb.state.rw_op.blocks_left = block_count;
    
        PT_INIT(&m_cb.state.pt);
        uint8_t command = (block_count > 1) ? CMD18 : CMD17;
        ret_code_t err_code = sdc_cmd(command, m_cb.state.rw_op.address, SDC_R1);
        APP_ERROR_CHECK(err_code);
    
        return NRF_SUCCESS;
    }

    1. You can use f_getfree() for this. I used the following code with the FatFs example in the latest SDK:
      FATFS *p_fs;
      DWORD fre_clust, fre_sect, tot_sect;
      
      /* Get volume information and free clusters of drive 1 */
      res = f_getfree("", &fre_clust, &p_fs);
      APP_ERROR_CHECK(res);
      
      /* Get total sectors and free sectors */
      tot_sect = (p_fs->n_fatent - 2) * p_fs->csize;
      fre_sect = fre_clust * p_fs->csize;
      
      
      /* Print the free space (assuming 512 bytes/sector) */
      NRF_LOG_INFO("%10lu KiB total drive space.\n%10lu KiB available.\n", tot_sect / 2, fre_sect / 2);

      This will give the remaining capacity in number of clusters, so it will depend a bit on the cluster size. This is the output I get with a 8GB SD-card:
      <info> app: FATFS example started.
      <info> app: Initializing disk 0 (SDC)...
      <info> app: Capacity: 7580 MB
      <info> app: Mounting volume...
      <info> app:    7760896 KiB total drive space.
         7760800 KiB available.
      <info> app: 
       Listing directory: /
          33578  NORDIC.TXT<info> app: Writing to file NORDIC.TXT...
      <info> app: 250 bytes written.
      
    2. How do you get into the state where this error is reported? I cannot remember to have seen this when running the FatFs examples. Note that there are a bug related to reinitialization in older SDK versions, which may be related. This is fixed in the latest SDK release.
Reply
    1. You can use f_getfree() for this. I used the following code with the FatFs example in the latest SDK:
      FATFS *p_fs;
      DWORD fre_clust, fre_sect, tot_sect;
      
      /* Get volume information and free clusters of drive 1 */
      res = f_getfree("", &fre_clust, &p_fs);
      APP_ERROR_CHECK(res);
      
      /* Get total sectors and free sectors */
      tot_sect = (p_fs->n_fatent - 2) * p_fs->csize;
      fre_sect = fre_clust * p_fs->csize;
      
      
      /* Print the free space (assuming 512 bytes/sector) */
      NRF_LOG_INFO("%10lu KiB total drive space.\n%10lu KiB available.\n", tot_sect / 2, fre_sect / 2);

      This will give the remaining capacity in number of clusters, so it will depend a bit on the cluster size. This is the output I get with a 8GB SD-card:
      <info> app: FATFS example started.
      <info> app: Initializing disk 0 (SDC)...
      <info> app: Capacity: 7580 MB
      <info> app: Mounting volume...
      <info> app:    7760896 KiB total drive space.
         7760800 KiB available.
      <info> app: 
       Listing directory: /
          33578  NORDIC.TXT<info> app: Writing to file NORDIC.TXT...
      <info> app: 250 bytes written.
      
    2. How do you get into the state where this error is reported? I cannot remember to have seen this when running the FatFs examples. Note that there are a bug related to reinitialization in older SDK versions, which may be related. This is fixed in the latest SDK release.
Children
  • Thanks for this example code. 

    1. How do you know the sector size, and is it likely to be variable between different SD cards?

    2. It seemed to happen randomly, so it is hard to say. It could very well be related to that other bug, but the behavior I saw was different - sometimes it does fail to initialize, but it doesn't continue to fail if it fails on first init. But maybe this is only because of my "bandaid" bugfix? I haven't had any issues since I put in this fix. 

    1. Looks like most are 512 bytes, but you should be able to check it like this:
      DWORD sector_size = 0;
      res = disk_ioctl(0, GET_SECTOR_SIZE, &sector_size);
      if(res != RES_OK)
      {
          NRF_LOG_INFO("Error getting sector size: %d", res);
      }
      NRF_LOG_INFO("sector_size: %d", sector_size);
    2. Ok, sounds like it could be something else. We would need to be able to reproduce it in order to report and fix the bug. If you are able to reproduce it later, please let me know the steps needed in order to get into this state.
Related