Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

FatFs and SD-Card: Read Request not finishing

Hi,

we are using the nRF52 DK (nRF52832) to read data from the i2s bus and store it as a wave-file on the connected SD-Card. When pressing button 1 on the DK, recording starts. When button 1 is pressed again, recording stops and the file should be closed. However, before we can close the file, we need to update the wave-header with the actual size of the file. For doing this, we use f_lseek to move the read/write pointer to the correct location. However during this operation something fails and the programm does not continue. I.e. it keeps running or is "waiting" for something.

As far as I could debug the problem the read/write pointer is changed correctly. The problem occurs at this line (nr. 4071)

if (disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);	/* Fill sector cache */

in ff.c. the disk_read function is defined in diskio_blkdev.c (line 188). The program stops at this point (line 8 in the code embedded here): 

 m_drives[drv].busy = true;
    ret_code_t err_code = nrf_blk_dev_read_req(m_drives[drv].config.p_block_device, &req);

    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)
        {
            return RES_OK;
        }
    }
    return RES_ERROR;

It seems that there is a disk I/O operation going on and not finishing, which causes the program to repeatedly call the wait function.

Since this function itself is called multiple times before we start recording (e.g. to list the content of the SD-card), I assume that our error is how we are handling the file or how we are writing to it. Therefore, I add the main functions of our program below:

// global variables
static FIL file;
uint32_t bytes_written;
FRESULT ff_result;

int main(void) {

  uint32_t err_code = NRF_SUCCESS;
  uint8_t sample_data;
  uint8_t response;
  bool detected_device = false;

  static struct tm time_keep;

  static struct tm time_fetched;

  uint8_t write_data[10] = {0x58}; // Data array to be written to the RTC slave
                                   // First register address in RTC slave to write to/read from

  uint8_t read_data[10]; // Data array to be read from the RTC slave

  APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
  NRF_LOG_DEFAULT_BACKENDS_INIT();

  fatfs_init();
  write_PCM_header(&file, FrameCount);
  twi_configuration();
  gpio_init();

  // Fs = 44.5kHz if 32DIV15
  // Fs = 31.75 if 32DIV21
  // Fs = 15.87KHz if 32DIV42
  nrf_drv_i2s_config_t config = NRF_DRV_I2S_DEFAULT_CONFIG;
  config.sdin_pin = I2S_SDIN_PIN;
  config.sdout_pin = NRFX_I2S_PIN_NOT_USED;
  config.mck_pin = NRFX_I2S_PIN_NOT_USED;
  config.mck_setup = NRF_I2S_MCK_32MDIV15;
  config.ratio = NRF_I2S_RATIO_48X;
  config.format = NRF_I2S_FORMAT_I2S;
  config.sample_width = NRF_I2S_SWIDTH_24BIT;
  config.channels = NRF_I2S_CHANNELS_LEFT;

  err_code = nrf_drv_i2s_init(&config, i2s_data_handler);
  APP_ERROR_CHECK(err_code);

  for (;;) {

    mode = 0x00;              // standby/stop mode
    m_blocks_transferred = 0; // counter to keep no of blocks transfer
    mp_block_to_check = NULL;

    // Wait for an event.
    __WFE();
    // Clear the event register.
    __SEV();
    __WFE();

    while (mode == 0x01) {

      if (init_file) {
        init_file = 0;
      }
      if (mp_block_to_check) {
        check_rx_data(mp_block_to_check);
        mp_block_to_check = NULL;
      }
      if (mode != 0x01) {
        break;
      }
    }
  }
}

static void fatfs_init() {

  static FATFS fs;
  static DIR dir;
  static FILINFO fno;

  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));

  printf("Initializing disk 0 (SDC)...");
  for (uint32_t retries = 3; retries && disk_state; --retries) {
    disk_state = disk_initialize(0);
  }
  if (disk_state) {
    printf("Disk initialization failed with disk state: %d", disk_state);
    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;
  printf("Capacity: %d MB", capacity);

  printf("Mounting volume...");
  ff_result = f_mount(&fs, "", 1);
  if (ff_result) {
    printf("Mount failed.");
    return;
  }
}

// handling the button input
void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
  nrf_drv_gpiote_out_toggle(PIN_OUT);

  if (mode == 0x00) {

    nrf_drv_i2s_buffers_t const initial_buffers = {
        .p_tx_buffer = NULL,
        .p_rx_buffer = m_buffer_rx[0],
    };

    ret_code_t err_code = nrf_drv_i2s_start(&initial_buffers, I2S_DATA_BLOCK_WORDS, 0);
    APP_ERROR_CHECK(err_code);

    mode = 0x01; // Recording mode
  } else if (mode == 0x01) {
    mode = 0x00; // Stop recording
    nrf_drv_i2s_stop();
    bsp_board_leds_off();
    // nrf_delay_ms(PAUSE_TIME);
    printf("closing file \n");

    //Closing the file after re-writing RIFF with total file size
    close_file();
  }
}

static void check_rx_data(uint32_t const *p_block) {
  ++m_blocks_transferred;

  if (!m_error_encountered) {
    m_error_encountered = !check_samples(p_block);
  }

  if (m_error_encountered) {
    bsp_board_led_off(LED_OK);
    bsp_board_led_invert(LED_ERROR);
  } else {
    bsp_board_led_off(LED_ERROR);
    bsp_board_led_invert(LED_OK);
  }
}

static bool check_samples(uint32_t const *p_block) {
  //each data word contains one 24-bit samples
  uint16_t i;
  uint8_t decomp_sample[DATA_BLOCK_WORDS_IN_BYTES];
  uint8_t const *p_decomp = &decomp_sample[0];

  printf("Checking samples \n");

  for (i = 0; i < I2S_DATA_BLOCK_WORDS; ++i) {
    uint32_t const *p_word = &p_block[i];

    //Decompose 32 bit data to 8 bit data and store only 24 bit data discarding the msb 8bits.
    decomp_sample[3 * i + 0] = ((uint8_t *)p_word)[0];
    decomp_sample[3 * i + 1] = ((uint8_t *)p_word)[1];
    decomp_sample[3 * i + 2] = ((uint8_t *)p_word)[2];
  }

  //Writing the decomposed block to the file.
  write_the_block(p_decomp);
  return true;
}

static bool write_the_block(uint8_t const *p_decomp) {

  ff_result = f_write(&file, p_decomp, DATA_BLOCK_WORDS_IN_BYTES, (UINT *)&bytes_written);

  if (ff_result != FR_OK) {
    return false;
  } else {
    //NRF_LOG_INFO("%d bytes written.", bytes_written);
    printf("%d bytes written. \n", bytes_written);
    printf("Total file size = %d \n", f_size(&file));
    return true;
  }
}

void close_file(void) {

  uint32_t total_size = f_size(&file);
  uint32_t chunky;
  chunky = (total_size - 8);
  ff_result = f_lseek(&file, 4);

  if (ff_result != FR_OK) {
    printf("Seek failed");
  }
  ff_result = f_write(&file, &chunky, sizeof(chunky), (UINT *)&bytes_written);
  if (ff_result != FR_OK) {
    printf("Write failed");
  }

  chunky = total_size;
  ff_result = f_lseek(&file, 40);
  if (ff_result != FR_OK) {
    printf("Seek failed");
  }
  ff_result = f_write(&file, &chunky, sizeof(chunky), (UINT *)&bytes_written);
  if (ff_result != FR_OK) {
    printf("Write failed");
  }

  (void)f_close(&file);
}

The problem occurs when the f_lseek in line 194 (of the embedded code above) is called.

I'm using nRF5 SDK 17.1.0 and Segger Embedded Studio. 

I will be happy to provide more information, I tried to not overload this post.

Any help or hints are highly appreciated!

Parents Reply Children
  • Hi Sigurd,

    we also had the problem you mentioned above earlier, it might be of help in the future. However, we now found that our code (developed using SDK 17.0.0) is working when we compile it using SDK 16.0.0 and Segger emStudio 4.52. Other combinations (e.g. SDK 16.0.0 and emStudio 4.18 or SDK 17.0.2 and emStudio 4.52/ 5.42 etc.) do not work. Honetly, I have no clue why, my guess it that different versions of emStudio come with different toolchains (even though the GNU version is always 4.2.1). 

    For us, the problem is solved for now, but we might have to port our code to a newer SDK version later on. Do you have a best practise for that, like starting from scratch using an example project included in the newer SDK?

    Thank you and regards!
    Moritz

  • In my experience, it should be rather straight forward to transfer from 16.0.0. to 17.0.2.
    So I would try just copying first.

    However, if there are errors using this method, they could be quite hard to track down.
    In such a case, it would likely be faster to start from scratch in the newer SDK.
    So if facing errors, I would not spend too much time on the debugging and just start from scratch(or from an example).

    Regards,
    Sigurd Hellesvik

Related