greiman / SdFat

Arduino FAT16/FAT32 exFAT Library
MIT License
1.05k stars 497 forks source link

STM32F1 SPI-Speed issues #413

Open nablabla opened 1 year ago

nablabla commented 1 year ago

Hi, I would like to read stuff fast, but I think something is not right here. I am using the STM32F103 (bluepill) Arduino framework, PlatformIO platform = ststm32 board = bluepill_f103c8 framework = arduino upload_protocol = stlink build_flags = '-D SPI_DRIVER_SELECT=0' '-D SD_FAT_TYPE=1' '-D ENABLE_EXTENDED_TRANSFER_CLASS=1' '-D ENABLE_DEDICATED_SPI =1' lib_deps = adafruit/SdFat - Adafruit Fork@^2.2.1 [...] I use some generic MicroSD Card Adapter v1.0 11/01/2013 from CATLEX I have files with about 4800Bytes on my SD card (32gb SDHC Class10, but I tried others as well, they work in gopro and provide ~50mbps) I only need to read these 4.800KB in less than say about 5ms, which I think should work or even much faster. But it takes 45ms that is kind of long in my opinion. And the time it takes grows

SdFat32 sd;
SpiPort_t spi2 = SpiPort_t(PB15, PB14, PB13);
SdSpiConfig config = SdSpiConfig(PB12, 0, SPI_FULL_SPEED, &spi2);
sd.begin(config);
while(true){
  digitalWrite(PIN_DT_READING, HIGH);  // for time measurement
  File32 file = sd.open(DATA_FILE_PATH);
  uint16_t *data[2400]; // I want 16bit/words, not bytes so I store it here
  int data_read = file.readBytes((uint8_t *)data, 4800); // returns 4800 [bytes read]
  digitalWrite(PIN_DT_READ, LOW); // for time measurement (pin is high for ~60ms)
}

sd.vol()->fatType() returns 32 sd.hasDedicatedSpi() returns 1 When I look at the MISO2/MOSI2/SCK2/CS pins with a logic analyzer I see that the clock is not regularly beating but chunked, looks like that when zooming in: grafik

And half of the time it is dilly dally doing nothing and chip enable is inactive. grafik

I don't know what to do, I think if everything is set up correctly it should work much better.

The time it takes i more after each readBytes call. It starts with 45ms and goes up to about 290ms, then suddenly back to 55ms

Moreover I found out something verry bizzare, when I instead call "file.readBytes((uint8_t *)data, 4801);" (pretending that the target data buffer is bigger than the data in the file) then the whole process works as well and fills 4800 bytes as the file only has that much, but takes ~1050ms instead, which is super extremely strange in my opinion, maybe some problem with the filestream class, and maybe a missing flush()?

Can I provide more details? What hints that my setup is wrong: The compiler flag STM32F1 is not set (grayed out), but when I set it manually it does not compile (m_spi->dmaTransfer not found)

The docs say a sentence that i don't really understand: """The types for the classes SdFat and File are defined in SdFatConfig.h. The default version of SdFatConfig.h defines SdFat to only support FAT16/FAT32. """ Also in the html docs I stumbled about this """You must manually install SdFat by renaming the download folder SdFat and copy the SdFat folder to the Arduino libraries folder in your sketchbook folder""". Also not shure about that, I guess it only affects ArduinoIDE users. The html docs also list the "STM32Test - Example use of two SPI ports on an STM32 board." which is no longer supported to what I have read in the issues.

Has anyone an idea how to use it with stm32? Where can I try version 1?

greiman commented 1 year ago

lib_deps = adafruit/SdFat - Adafruit Fork@^2.2.1

Contact Adafruit.

https://github.com/adafruit/SdFat

Adafruit has their own mods to SdFat so I can't help you with their versions and their version numbers will diverge from mine.

nablabla commented 1 year ago

ah, no I changed that already, sorry, I copy pasted that. It is lib_deps = greiman/SdFat @ ^2.2.0 adafruit/Adafruit SSD1306@^2.5.7 And their branch is mostly the same and behaves identical in this regard

greiman commented 1 year ago

The The official STMicroelectronics Arduino support package is hopeless. You may not get the performance of an Arduino Uno.

You must begin by replacing this call to Stream::readBytes(char buffer, size_t length): ` int data_read = file.readBytes((uint8_t )data, 4800); // returns 4800 [bytes read]`

By

int data_read = file.read(data, 4800);

This will still be slow on STM32 because the SPI transfer will be mostly dead time between bytes in the STMicroelectronics SPI Arduino driver.

nablabla commented 1 year ago

oh wow okay. I replaced this as you said. It it twice as fast, takes about 20ms, but then gets slower up to 145ms :( Then I moved the file.open out of the measurement resulting in 14ms, which I can live with. So I switched to a single big file, rather than 640 single files and and keep it "open" the whole time. Now it is 12~14ms consistent. I can live with that. I am just curious if I can further improve it. However it is solved, thank you for solving my problem :=)

The weired long idleing is gone, there are about 10 breaks of 0.14ms. And it looks like there are now clock cycle chuncks of 8 bytes (rather than 4). grafik Is that how long it takes to store that byte? A micro second?

I am not too fond of arduino except the convenience methods and pin names. "The official STMicroelectronics Arduino support package". Is there something better? I can go stm32 or something, but it is more tedious i guess and I can not use this lib right?

greiman commented 1 year ago

You must not open files to achieve speed. FAT does not index directories so a linear search is done in open. This means all 640 files must be scanned in open for the last file.

There was a better package for STM32F1 and STM32Test uses that package. https://github.com/rogerclarkmelbourne/Arduino_STM32 It no longer has much use or support since the STMicroelectronics package was released.

STMicroelectronics uses a wrapper for STM32Cube that does not use SPI DMA and restricts the clock rate. It has a slow byte-at-a-time loop for SPI block transfers.

Looks like the clock on the SPI port you are using is limited to about 9 MHz with 1.25 µs gaps. The clock on APB1 is 36 MHz but it looks like the SPI divisor is four.

The other SPI port is on the 72 MHz APB2 bus but STMicroelectronics may limit its speed also.

greiman commented 1 year ago

You are getting about 400 KB/sec. That's less than half the rate I get on an 8-bit AVR Uno. Shame on STMicroelectronics.

On Teensy 4.1 I get 22 MB/sec read/write with a custom 4-bit SDIO driver. A factor of over 50 faster.

For STM32 I like ChibiOS but I only have a private version of SdFat for it.