Skip to main content
AFahr.1
Associate III
May 26, 2021
Question

STM32L496 - SD Card SDIO write speeds (FreeRTOS, FATFS and USB), how to get "fast" (> 100kbytes/sec) throughput?

  • May 26, 2021
  • 12 replies
  • 6414 views

Hi all,

I need to stream data into an SD card, at about 25KBytes/sec max. Currently I'm stuck at 11kBytes/sec, but the SDIO data transfer works, kind of. I'm using an STM32L496 board, FATFS and FreeRTOS, as well as USB.

The USB part works actually really well. Every time I plug in I'm able to download at ~400kBytes/sec and copy to the SD card at say ~200Kbytes/sec. I don't need more than that.

When the USB is unplugged, I copy data from different sensors onto a StreamBuffer (FreeRTOS), and once I get 512 bytes, I copy them over to the SD card using f_write(...) through an additional buffer. I have several issues with this:

  • If I set the clock on the SD card to be less than 2, it fails very often, seconds after it starts
hsd1.Init.ClockDiv = 2;

  • If I set the bus to be 4 bits wide with the clock 2 or more, f_write returns 1 (DISK ERROR) after about 6 seconds.
  • If I set the bus to be 1 bit wide with clock 2 or more, f_write returns 1 after a few minutes, 3-5.
  • My SD write function for FatFS is below:
DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
{
	DRESULT res = RES_ERROR;
	uint32_t timeout;
	uint32_t time;
	HAL_StatusTypeDef retval;
	HAL_SD_CardStateTypedef cardState;
	bool sd_wait = true;
 
 
	timeout = count * 500; //Timeout in MS per block
	sd_wait = true;
 
	if(true == sd_card_ready)
	{
	 	retval = HAL_SD_WriteBlocks(&hsd1, (uint8_t *)buff, sector, count, timeout);
	 	cardState = HAL_SD_GetCardState(&hsd1);
 
	 	if (HAL_OK == retval)
		{
			time = HAL_GetTick();
			while(true == sd_wait)
			{
				cardState = HAL_SD_GetCardState(&hsd1);
 
				if(HAL_SD_CARD_TRANSFER == cardState)
				{
					sd_wait = false;
					res = RES_OK;
				}
 
				if((HAL_GetTick() - time) > timeout)
				{
					sd_wait = false;
					HAL_SD_Abort(&hsd1);
				}
			}
		}
	}
 
 return res;
}

The above code is the same for the USB transfer.

I'm at a loss a bit, I thought writing a block (512bytes) of data would increase the data throughput to the SD card, and I have a lot of contiguous data blocks, all the time. Data is constantly read in at about 11 KBytes/sec.

I read somewhere else that doing a pre-check on the data alignment before the "HAL_SD_WriteBlocks" call would help. I might need to try that out.

If anyone can point me in the right direction with examples, links, explanations, etc, I'd really appreciate it. I am able to buffer up to ~500ms of data if necessary... maybe more, how can I detect or recover from a slow (250ms or so) write cycle?

Cheers,

Alberto

    This topic has been closed for replies.

    12 replies

    Tesla DeLorean
    Guru
    May 26, 2021

    The 512-byte writes to a file need to be on 512-byte boundaries within the file. Watch for any header structures you might write breaking this.

    Spanning writes will cause a lot of unnecessary Read-Write churn.

    Small writes below 512-byte have significant performance issues within FatFs.

    Writes of 512-byte are the least efficient on the SD-Card, it is a single sector, ideally you want to write a cluster to achieve maximum throughput on the FatFs and SDIO/SDMMC level.

    The L4 should be able to sustain multi MBps continuous writes to an SD Card, generally the card limits top speed. USB-HS likely limits to about 700 KBps, make sure the MSC packet size is as large as possible, ie not just 512 bytes

    Is this a board of your own design?

    If you can't clock the interface at 25 MHz / 4-bit there's some issue in the wiring, termination or socket. My understanding of the MicroSD card specs is that compliant cards minimally support 50 MHz, that's not to say there aren't sub-par cards out there.

    The L496 supports SDMMC DMA

    The SD Cards have zero tolerance of the application wandering off-task during the data transfer phase. This could be a particular issue if you yield mid-polled transfer.

    Using ping-pong buffers so you can concurrently fill new buffers whilst writing the full ones.

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    TDK
    May 26, 2021

    The HAL library isn't implemented for very high transfer speeds. It will have a buffer underrun or overrun and produce a hard to find error that is typically obfuscated (e.g. DISK ERROR) at the user level. Compiling in Release mode will help mitigate. Modifying HAL code to be faster will help more.

    Putting a bus monitor on the line will help debug the issue, although it is typically more work than most people are willing to put forth.

    "If you feel a post has answered your question, please click ""Accept as Solution""."
    AFahr.1
    AFahr.1Author
    Associate III
    May 26, 2021

    @Community member​ Thanks for that.

    I'm not sure if the 512 byte writes are within the boundary of the file. The data being written onto the file is from the buffer where it's collected, and usually in 512 byte chunks or multiples, except when closing the file (tail end of the stream).

    It is a board of our own design, and yes, there might be some wiring issues, although, as mentioned before, when the USB is plugged, we get roughly 700KBps read speeds and no transfer errors.

    The card should be ok, it's one of the better specced SanDisks.

    I will have a look at DMA transfers, that might be what's breaking the transfer, the wandering off phase.

    Cheers,

    Alberto

    AFahr.1
    AFahr.1Author
    Associate III
    May 27, 2021

    Thanks a lot for all your comments so far.

    I realised one mistake I made on the setup of the SD card.

     hsd1.Instance = SDMMC1;
     hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
     hsd1.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE;
     hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
     hsd1.Init.BusWide = SDMMC_BUS_WIDE_1B;
     hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_ENABLE; ///< It was set to DISABLE!
     hsd1.Init.ClockDiv = 0;

    I changed line 6 to ENABLE and all started working beautifully. I recently ran a test overnight, and there were no transfer errors over 640 MBytes. I'll increase the speed, but it's looking good at the moment.

    Cheers,

    Alberto

    Jtron.11
    Senior
    June 14, 2021

    Hi Alberto,

    May I ask in this project, when you are done writing t to SD card using FatFS, are you be able to retrieve the data from PC using USB to read data from your SD card? May I ask if you are using USB MSC or USB CDC implementation?

    Tesla DeLorean
    Guru
    June 14, 2021

    Isn't a USB MTP implementation a better route?

    Having two devices share the media is fraught with issues, caching on the PC side (Windows/Linux) needs to be managed. One device should own the media, the other needs to deal with a NOT READY issue, and retry (FatFs and the user application side needs to be a lot smarter), and also be able to handle a MEDIA CHANGED event, where either device need to reassess the current content of the media/file system.

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    Jtron.11
    Senior
    June 15, 2021

    the flexibility of storage mode extends not only to what you can do, but how you can do it; it's accessible to any software that can access a filesystem (your favorite file browser, rsync, etc). MTP can only be accessed by software that speaks MTP (although there are ways to partially work around this).

    Jtron.11
    Senior
    June 16, 2021

    Alberto,

    May I ask you do you measure the throughput "MSC is used for data transfer. We get a stable throughput of about 500 - 700 KBytes per second" ?

    I am waiting for the hardware to come in order to implement the USB MSC + uSD card.

    If my application only need to get the file from uSD card that store the logging from USB, I don't actually need the CDC VCOM like your application is that correct? I assume you need VCOM in your application because you actually want to send commands to your system. If you don't need to send in the commands, you are off with USB MSC is enough right?

    AFahr.1
    AFahr.1Author
    Associate III
    June 16, 2021

    Hi JTron,

    No, you don't need the VCOM interface if you're not planning to send any commands to the micro. You only need to implement with CubeMX the MSC interface on the USB. In that case, you get a USB drive and that's it.

    We get about 700 kB/sec on raw data transfer between PC (Windows) and micro. Takes about 20 seconds or so to copy a 10MB file. We only use the Windows transfer window to gauge the transfer rate, that's good enough for us.

    Cheers,

    Alberto

    Jtron.11
    Senior
    June 17, 2021

    Alberto,

    Did you have a chance to throw in the logic analyzer to capture the signal of the USB detection pin at the power up? Chance there are periods of times the signal was not stable because the STM32 going thru the reset and GPIOs going thru the initialization? I learned the hard way before when I took think for granted and think the signal is on the level that I think it is, but as soon as I got the capture, my signal went up and down and vice versa for so many time especially when I utilize the reset pin input to STM32 mcu.

    AFahr.1
    AFahr.1Author
    Associate III
    June 17, 2021

    No, we didn't. The USB port (the host PC) provides our board with 5V over a 4.7K resistor in series into pin PA9 (I believe). The power to the board is generated independently. PA9 is read in the background at a frequency of 50 Hz or so, and that starts or stops the USB peripheral accordingly.Cheers,

    Alberto

    AFahr.1
    AFahr.1Author
    Associate III
    June 17, 2021

    Sorry, images missing...

    0693W00000BbCtkQAF.png0693W00000BbCtfQAF.png

    Jtron.11
    Senior
    June 17, 2021

    I believe if you have the pull low in the USB_VBUS it will help to prevent those glitches, in my case VBUS_SENSE is the same as your USB_ VBUS

    Jtron.11
    Senior
    June 17, 2021

    0693W00000BbDUbQAN.png

    Jtron.11
    Senior
    June 21, 2021

    Alberto,

    Just a quick updates, so my SDIO adapter came, and I am successfully implemented SDIO 4 bit DMA FatFS. So I already checkout SDMMC, SPI and before was the USB device MSC.

    When I put the 2 USB MSC and SDIO together, nothing work so I back out everything and just try to implement USB device MSC only. I successfully test the SRAM and SPI uSD before.

    The matter is getting worse now my uUSB won't even get detection when I plug in the USB cable.

    I tried on my eval Nucleo-F722ZE (2 different boards) the tutorial from STM

    https://www.youtube.com/watch?v=dC0d8CVEPrQ

    Or from another instruction

    https://www.youtube.com/watch?v=Us_sTdGGcOQ

    But both of them not working on my eval board regardless.

    I just want to have the SRAM USB MSC working. Do you have any suggestion or pointers? By the way, if we use the eval, we don't need to enable PA9 for USB_VBUS enable correct?

    Thank you so much for your helps.