Skip to main content
Hau Lam
Associate III
May 8, 2018
Question

USB Mass storage + SDMMC read write too slow

  • May 8, 2018
  • 11 replies
  • 4500 views
Posted on May 08, 2018 at 13:11

i using STMF779I-EVAL board and try to  implement USB Mass Storage + SD Card . But the read/write speed i am getting when write/read from Window PC is too slow. 

Read : 1MB/s; Write : 200KB/s

Even I already used DMA mechanism to speed up the SDMMC driver .

This is my implementation for read/write

int8_t STORAGE_Write_HS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)

{

//printf('STORAGE_Write_HS \n');

HAL_SD_CardStateTypeDef sd_card_state_return;

HAL_SD_StateTypeDef state_return;

uint32_t timeout = 0;

if(HAL_OK != HAL_SD_WriteBlocks_DMA(&hsd1, buf, blk_addr, (uint32_t) blk_len))

{

printf('Error : STORAGE_Write_HS failed Err: %u, blk_addr : %u ; blk_len : %u\n', hsd1.ErrorCode, blk_addr, blk_len);

return USBD_FAIL;

}

// Callback 

HAL_SD_TxCpltCallback increare writestatus  to check writing status

while (writestatus == 0)

{

}

writestatus = 0;

/* Wait until SD card is ready to use for new operation */

while (HAL_SD_GetCardState(&hsd1) != HAL_SD_CARD_TRANSFER)

{

}

return (USBD_OK);

/* USER CODE END 14 */

}

void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)

{

//printf('HAL_SD_TxCpltCallback \n');

if(hsd->Instance == SDMMC1)

{

writestatus = 1;

}

}

I think that the HAL_SD_GetCardState(0 to long time to change state to HAL_SD_CARD_TRANSFER.

Could you let me know what is the root cause and is there any way to improve the speed ?

Thank you very much.

    This topic has been closed for replies.

    11 replies

    Hau Lam
    Hau LamAuthor
    Associate III
    May 16, 2018
    Posted on May 16, 2018 at 11:58

    Could somebody help to take a look ? 

    Tesla DeLorean
    Guru
    May 16, 2018
    Posted on May 16, 2018 at 13:52

    >> STMF779I-EVAL

    Sorry not a board that I have.

    Would check to see the maximum block requested for read/write operations, and check if descriptors/settings reported to PC limit block count or transfer size.

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    Hau Lam
    Hau LamAuthor
    Associate III
    May 17, 2018
    Posted on May 17, 2018 at 04:56

    Hi

    Turvey.Clive

    ‌,

    I checked block len in functionint8_t STORAGE_Write_HS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)

    it always write 1 block /time, and block size is 512 bytes.

    Is it the problem cause writing slow issue ? and how can i improve this ?

    Tesla DeLorean
    Guru
    May 18, 2018
    Posted on May 18, 2018 at 04:15

    You'd want to make sure there isn't an issue with using or allocating that much memory. Where it falls in the memory space may also be problematic. Issues with caching, use of DMA, use of DTCMRAM, etc.

    If the size is reported somewhere, there might be a 16-bit limit.

    32KB should be sufficient, consider the speed the SDMMC is being clocked. Most microSD cards are supported to clock a 50 MHz on the bus (not 24 MHz the 48 MHz gets you).

    Check the clock source selected in RCC->DCKCFGR2, you should be able to select the 200 MHz SYSCLK, not the ~48 MHz of the PLL_Q or PLLSAI_P. You'll need to change the SDMMC ClockDiv appropriately.

    http://www.st.com/content/ccc/resource/technical/document/reference_manual/group0/96/8b/0d/ec/16/22/43/71/DM00224583/files/DM00224583.pdf/jcr:content/translations/en.DM00224583.pdf

     

    void plldumpf7(void) //

    mailto:sourcer32@gmail.com

    {

      uint32_t vcoinput, vco;

      uint32_t oscsource;

      uint32_t dckcfgr2 = RCC->DCKCFGR2;

      uint32_t pllcfgr = RCC->PLLCFGR;

      uint32_t plli2scfgr = RCC->PLLI2SCFGR;

      uint32_t pllsaicfgr = RCC->PLLSAICFGR;

      uint32_t pllp, pllq, pllr;

      uint32_t p, q, r;

      uint32_t plli2s_p, plli2s_q, plli2s_r;

      uint32_t pllsai_p, pllsai_q, pllsai_r;

      /* Configure the PLLSAI division factor */

      /* PLLSAI_VCO Input  = PLL_SOURCE/PLLM */

      if((pllcfgr & RCC_PLLCFGR_PLLSRC) == RCC_PLLSOURCE_HSI)

        oscsource = HSI_VALUE; /* In Case the PLL Source is HSI (Internal Clock) */

      else

        oscsource = HSE_VALUE; /* In Case the PLL Source is HSE (External Clock) */

      vcoinput = (oscsource / (uint32_t)(pllcfgr & RCC_PLLCFGR_PLLM));

      vco = (vcoinput * ((pllcfgr & RCC_PLLCFGR_PLLN) >> 6));

      pllp = (((pllcfgr & RCC_PLLCFGR_PLLP) >> 16) + 1) * 2; // SYSCLK

      p = vco / pllp;

      pllq = (pllcfgr & RCC_PLLCFGR_PLLQ) >> 24; // PLL48

      q = vco / pllq;

      pllr = (pllcfgr & 0x70000000) >> 28; // Not on F74xx RCC_PLLCFGR_PLLR

      r = vco / pllr;

      printf('PLL    P:%9d Q:%9d R:%9d\n', p, q, r);

      vco = (vcoinput * ((plli2scfgr & RCC_PLLI2SCFGR_PLLI2SN) >> 6));

      pllq = (((plli2scfgr & RCC_PLLI2SCFGR_PLLI2SP) >> 16) + 1) * 2;

      plli2s_p = vco / pllp;

      pllp = (plli2scfgr & RCC_PLLI2SCFGR_PLLI2SQ) >> 24;

      plli2s_q = vco / pllq;

      pllr = (plli2scfgr & RCC_PLLI2SCFGR_PLLI2SR) >> 28;

      plli2s_r = vco / pllr;

      printf('PLLI2S P:%9d Q:%9d R:%9d\n', plli2s_p, plli2s_q, plli2s_r);

      vco = (vcoinput * ((pllsaicfgr & RCC_PLLSAICFGR_PLLSAIN) >> 6));

      pllq = (((pllsaicfgr & RCC_PLLSAICFGR_PLLSAIP) >> 16) + 1) * 2;

      pllsai_p = vco / pllp;

      pllp = (pllsaicfgr & RCC_PLLSAICFGR_PLLSAIQ) >> 24; // 2..15

      pllsai_q = vco / pllq;

      pllr = (pllsaicfgr & RCC_PLLSAICFGR_PLLSAIR) >> 28; // 2..7

      pllsai_r = vco / pllr;

      printf('PLLSAI P:%9d Q:%9d R:%9d\n', pllsai_p, pllsai_q, pllsai_r);

      // DCKCFGR2 bit 27 PLL48CLK 0=PLLQ, 1=SAIP

      // DCKCFGR2 bit 28 SDMMCSEL 0=PLL48, 1=SYSCLK (bit 29 for SDMMC2)

      {

        uint32_t pll48clk = ((dckcfgr2 & (1<<27)) ? pllsai_p : q);

        uint32_t sdmmc_clkcr = SDMMC1->CLKCR;

        uint32_t sdclk = ((dckcfgr2 & (1<<28)) ? p : pll48clk) / ((sdmmc_clkcr & 0xFF) + 2);

        printf('PLL48CLK %9d, %6.2lf MHz\n', pll48clk, (double)pll48clk*1e-6);

        printf('SDCLK    %9d, %6.2lf MHz\n', sdclk, (double)sdclk*1e-6);

      }

      {

        uint32_t dckcfgr1 = RCC->DCKCFGR1;

        uint32_t divr = 2 << ((dckcfgr1 & RCC_DCKCFGR1_PLLSAIDIVR) >> 16); // 2,4,8,16

        uint32_t lcdclk = pllsai_r / divr;

        printf('LCDCLK   %9d, %6.2lf MHz\n', lcdclk, (double)lcdclk*1e-6);

      }

    }

    >>Could you help me on this issue ? 

    Consider sponsoring my work/projects.

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    Hau Lam
    Hau LamAuthor
    Associate III
    May 18, 2018
    Posted on May 18, 2018 at 05:00

    Dear

    Turvey.Clive.002

    ‌,

    It actually the is a warning compiler when i set MSC_MEDIA_PACKET to 65536

    Look intousbd_msc_scsi.c there is 1 functionSCSI_Read10() itset hmsc->bot_data_length = MSC_MEDIA_PACKET; but

    bot_data_length is declared uint16_t only.

    So can i say that the current ST USB stack only support 32KB ?

    Consider sponsoring my work/projects.

    What do you mean

    Tesla DeLorean
    Guru
    May 18, 2018
    Posted on May 18, 2018 at 17:36

    I'm not paid by ST, if I've saved you man-hours or man-days of work, consider how you can help me.

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    James Murray
    Associate III
    May 18, 2018
    Posted on May 19, 2018 at 00:22

    On the STM32F769I-DISCO I had similar issues, the example code was very slow. Writes corrupted the card too.

    I have fixed the write corruption (must check no longer busy before returning) and boosted read speed to over 7MB/sec.

    My approach was to implement a simple read-cache and read in numerous blocks in one go.

    i.e. instead of requesting as single 512 byte sectors, I read 32x 512 byte sectors, store the result to RAM and then return a pointer to the start of the sector. On the next read, if that sector is already in RAM, I just return a pointer and don't do an SDcard read. This makes a BIG difference. Any writes invalidate the cache valid flag.

    I also found that the provided SD card read block code is issuing an initialisation command every time, that is not required and wastes time.

    I'm afraid I'm not sharing the code, but hopefully these notes should give you a start.

    James