I have a driver works on ESP32 in software SPI and also hardware SPI mode, but is very slow.
I think HAL mutex make this situation.
Do you use spi_master from esp-idf directly, or do you use the Arduino code? Also, in what sense is it slow? High latency, low clock speed, ...?
Just esp-idf
This is code
#define LCD_LED 5 // BL
#define LCD_RS 21 // RS/DC
#define LCD_CS 22 // CS/CE
#define LCD_RST 18 // TFT --RST
#define LCD_SCL 19 // TFT --SCL/SCK
#define LCD_SDA 23 // MOSI--->>TFT --SDA/DIN
spi_device_handle_t spi;
void spi_pre_transfer_callback(spi_transaction_t *t) {
gpio_set_level(LCD_RS, (int) t->user);
}
void LCD_GPIO_Init(void) {
esp_err_t ret;
spi_bus_config_t buscfg = {
.miso_io_num = -1,
.mosi_io_num = LCD_SDA,
.sclk_io_num = LCD_SCL,
.quadwp_io_num = -1,
.quadhd_io_num = -1 };
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 3600000, //Clock out at 10 MHz
.mode = 0, //SPI mode 0
.spics_io_num = LCD_CS, //CS pin
.queue_size = 7, //We want to be able to queue 7 transactions at a time
.pre_cb = spi_pre_transfer_callback, //Specify pre-transfer callback to handle D/C line
};
//Initialize the SPI bus
ret = spi_bus_initialize(HSPI_HOST, &buscfg, 1);
assert(ret==ESP_OK);
//Attach the LCD to the SPI bus
ret = spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
assert(ret==ESP_OK);
gpio_set_direction(LCD_RS, GPIO_MODE_OUTPUT);
gpio_set_direction(LCD_RST, GPIO_MODE_OUTPUT);
gpio_set_direction(LCD_LED, GPIO_MODE_OUTPUT);
}
esp_err_t SPI_WriteIndex(spi_device_handle_t spi, uint8_t cmd) {
esp_err_t ret;
spi_transaction_t t;
memset(&t, 0, sizeof(t)); //Zero out the transaction
t.length = 8; //Command is 8 bits
t.tx_buffer = &cmd; //The data is the cmd itself
t.user = (void*) 0; //D/C needs to be set to 0
ret = spi_device_transmit(spi, &t); //Transmit!
return ret;
}
esp_err_t SPI_WriteData(spi_device_handle_t spi, uint8_t *data, int len) {
esp_err_t ret;
spi_transaction_t t;
if (len == 0)
return ESP_FAIL; //no need to send anything
memset(&t, 0, sizeof(t)); //Zero out the transaction
t.length = len * 8; //Len is in bytes, transaction length is in bits.
t.tx_buffer = data; //Data
t.user = (void*) 1; //D/C needs to be set to 1
ret = spi_device_transmit(spi, &t); //Transmit!
return ret;
}
Fyi, code blocks need three backticks to work. I've been so bold to edit your comment. Also, again the question: in what aspect do you think the code is slow? What do you expect, what do you see?
Soft api faster than HW SPI on ESP32.
This is a driver for st7735r LCD chips,I had test this driver on STM32F103 ,HW SPI very fast.
I can see slower than STM32 use my eyes……
Also possible related to https://github.com/espressif/esp-idf/pull/336
You're still not clear what exactly is slow. I'm going out on a limb here and assume you mean the latency between transactions, not the clock speed or cpu use or anything else. If so, you want to pipeline your transmissions. Basically, you can do this by queueing a bunch of transmissions using spi_device_queue_trans and then later on waiting for them to finish using spi_device_get_trans_result.
negativekelvin: I'm pretty sure it's not. #336 only comes into play when receiving data as well, as far as I can see.
I'm sure not, I only run a task,just refresh the LCD,I also tried the queue, but it did not work.
I think my situation is similar to the following:
https://forum.arduino.cc/index.php?topic=437206.0
https://www.esp32.com/viewtopic.php?t=555
So I think this may be related to mutex
Okay, but then I will ask again: what is slow according to you, what speed do you expect and what speed do you get? Incidentally, there are no mutexes in the esp-idf SPI driver code (which is entirely separate from the Arduino code). Also, what do you try to transfer? You only posted some utility routines, but I have no idea what you feed into those.
I expect the speed to be 25 frames on a 160 * 128 pixel screen,If I set it wrong, give me an example please.
Whether there is full duplex?
What do you try to transfer? You only posted some utility routines, but I have no idea what you feed into those: entire frames, lines, single pixels, ...
I understand what you mean, I need to transfer the whole frame.
If there is no way, I try to use the soft SPI……
That would indeed help, yes. The SPI routine fires an interrupt when a transfer is finished. This will take some time to process. When you have more transactions queued, the interrupt can immediately start the next transfer, this is reasonably fast. When you have coded your transfers as above (with no queue), this is the worst case: the interrupt fires, sees there's nothing to be done, your program resumes, the transfer triggers another interrupt to queue the new data, the transaction finished causing another interrupt etc. You can probably do sw SPI as well, but be aware that probably hangs up the entire core in transmitting, which can kill the CPU-time available to other tasks.
I'll try it first
The SPI driver in ESP-IDF is quite flexible and thread-safe, but I also found it to be too slow for my purpose (also, banging out > 100 kB @ 30 MHz byte-by-byte, interrupted by some GPIO toggeling).
The cause for this is "slowness" is that between each single-byte transfer, the RTOS has to do some queueing. Then on top there's the "transfer end" interrupt, which again needs some time for queueing. Each queue access seems to take 1/CONFIG_FREERTOS_HZ = 1 ms @ CONFIG_FREERTOS_HZ=1000 for the context switch and _is_ secured by mutexes inside the FreeRTOS framework.
If I could bang out my bits without queuing, I'd be done in < 50 ms, using the SDK's queued driver it takes around 20 s.
My workaround was to take the esp32-hal-spi.c/h module combo from the arduino project, modifying a little, removing all MUTEX calls from spiWriteByte() which are supposed to make this call thread-safe and use it directly from my application in ESP-IDF. It works without queues, events, interrupts or mutexes and is _really_ fast.
Remember this module is not thread safe anymore and any of its functions must only be called from the same thread.
TO holzachr: yes!Yes, I used RTOS, how to solve this problem? I guess it should be for this reason
I found a way to solve this problem, that is, use the more underlying functions, as used in the emunes project.
Most helpful comment
The SPI driver in ESP-IDF is quite flexible and thread-safe, but I also found it to be too slow for my purpose (also, banging out > 100 kB @ 30 MHz byte-by-byte, interrupted by some GPIO toggeling).
The cause for this is "slowness" is that between each single-byte transfer, the RTOS has to do some queueing. Then on top there's the "transfer end" interrupt, which again needs some time for queueing. Each queue access seems to take 1/CONFIG_FREERTOS_HZ = 1 ms @ CONFIG_FREERTOS_HZ=1000 for the context switch and _is_ secured by mutexes inside the FreeRTOS framework.
If I could bang out my bits without queuing, I'd be done in < 50 ms, using the SDK's queued driver it takes around 20 s.
My workaround was to take the esp32-hal-spi.c/h module combo from the arduino project, modifying a little, removing all MUTEX calls from spiWriteByte() which are supposed to make this call thread-safe and use it directly from my application in ESP-IDF. It works without queues, events, interrupts or mutexes and is _really_ fast.
Remember this module is not thread safe anymore and any of its functions must only be called from the same thread.