-
Notifications
You must be signed in to change notification settings - Fork 7.6k
No DMA support for SPI in arduino-esp32? #4590
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
DMA will be added at some point but it's not an easy task because of some internal issues. For now you can use the IDF API for SPI and utilise DMA that way. |
I could not find it. |
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html |
Before you wonder, "RudolphRrr"is my account as well. With a simple SPI.transfer(data) executing a lot slower on the ESP32 than on the UNO, the SPI class kind of needs an update, and/or probably esp32-hal-spi.c. |
[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions. |
Not actually resolved but I worked around the issue by using ESP-IDF in the meantime. One thing that was interesting to note is that the time between two simple 8-bit transfers on ESP32-Arduino is ridiculously long, especially for a 240MHz controller, with 6µs. Yes, I am talking about the pause between sending two bytes, the time from one byte is finished and When claiming the bus with spi_device_acquire_bus() / spi_device_release_bus() this is even increased to 32µs. I switched over to use spi_device_polling_transmit() and it got a lot better - for whatever reason. So using simple SPI transfers with ESP-IDF is even slower than with ESP32-Arduino. At this point I consider SPI with the ESP32 to be severly broken. And for a reference, when using the Arduino SPI class on an Adafruit Metro M4 which is clocked at 120MHz, |
[STALE_CLR] This issue has been removed from the stale queue. Please ensure activity to keep it openin the future. |
[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions. |
[STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions. |
the base issues of very long inter-byte gaps is still valid. |
There are a couple of reasons why we do not have DMA support in Arduino yet. As you found out yourself, short transactions take a lot longer time to setup and execute with DMA and in Arduino we generally have many/many small transactions. LCD drivers write pixels, etc. On another topic, the DMA SPI on ESP32 is a bit harder to get going correctly and the IDF team had many headaches with it, otherwise the trick is to do the small transactions either not by DMA or by polling the ISR directly (instead of waiting for event in a task to fill the next buffer). |
I used no-dma transfers for the shorter transfers because most of these are read operations in my application and the result is needed immediately anyways, like checking if the display is busy or reading the touch data. But I also need to transfers buffers of 250...4k bytes and the option to do that with DMA would be nice, like how it is implemented for the Teensy 4 with a callback function. The issue however is not that DMA is taking a long time to setup in general, only the ESP-IDF needs endless cycles for everything. The code for this sequence essentially is: And I am currently running it on an ESP32-C3, a 160MHz RISC-V, so in absolute terms this still is rather slow. |
Wrapping this into SPI transactions will make it even faster, since BUS will need to be locked/unlocked only once. |
This is the proper call order to use best SPI in Arduino (and fastest on ESP32): // Lock the bus and select the slave device
SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0));
digitalWrite(EVE_CS, LOW);
// Transact the necessary data
SPI.transfer(&data, 4);
result = SPI.transfer(0x00);
// Deselect the slave device and release the bus
digitalWrite(EVE_CS, HIGH);
SPI.endTransaction();
// Return the result
return result; |
Or not always use SPI.beginTransaction() / SPI.endTransaction() which makes this even faster. :-) And using SPI.write32() is indeed a little faster. |
When you do not use transactions, SPI bus is locked/unlocked with each call to it's API functions, else it is locked/unlocked only once in a transaction. You have two calls to |
No, what I meant is that my code expects SPI.beginTransaction() has been called and it would not call it or SPI.endTransaction() if the frequency could be changed without calling these. |
Is the difficulty of SPI speed specific to the ESP32 or it also show up in S3, C3 etc.? |
The last tests I did was with the C3 so yes, the results should be the same across the family. And to make this clear, the implementation here for Arduino is faster now than using the ESP-IDF directly.
Every ms sounds like exclusive use of the SPI and if this is the case you do not need to use SPI.beginTransaction() / SPI.endTransaction() every time.
That should be more like less than 20µs. If you are using single transfers, try to switch over to a buffer transfer. |
Indeed I made a mistake in my measurement. There was actually three SPI transfers in each measurement: query the status bit, clearing interrupt in the sensor, and the actual data transfer. This adds up to the ~50us you observe. I am using esp_timer_get_time() to get the timing, as I currently do not have an oscilloscope handy. |
Neither the SPI class which is merely a wrapper or the underlying esp32-hal-spi.c seem to support DMA.
Is this observation correct?
Or is there a way to send a buffer over SPI with DMA without adding a third party library?
The text was updated successfully, but these errors were encountered: