Skip to content
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

HW SPI so slow,How to solve? (IDFGH-7973) #368

Closed
ghkjgod opened this issue Feb 21, 2017 · 19 comments
Closed

HW SPI so slow,How to solve? (IDFGH-7973) #368

ghkjgod opened this issue Feb 21, 2017 · 19 comments
Labels
Resolution: Done Issue is done internally Status: Done Issue is done internally

Comments

@ghkjgod
Copy link

ghkjgod commented Feb 21, 2017

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.

@Spritetm
Copy link
Member

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, ...?

@ghkjgod
Copy link
Author

ghkjgod commented Feb 21, 2017

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;
}

@Spritetm
Copy link
Member

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?

@ghkjgod
Copy link
Author

ghkjgod commented Feb 21, 2017

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……

@negativekelvin
Copy link
Contributor

Also possible related to #336

@Spritetm
Copy link
Member

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.

@ghkjgod
Copy link
Author

ghkjgod commented Feb 21, 2017

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

@Spritetm
Copy link
Member

Spritetm commented Feb 21, 2017

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.

@ghkjgod
Copy link
Author

ghkjgod commented Feb 21, 2017

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?

@Spritetm
Copy link
Member

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, ...

@ghkjgod
Copy link
Author

ghkjgod commented Feb 21, 2017

I understand what you mean, I need to transfer the whole frame.

@ghkjgod
Copy link
Author

ghkjgod commented Feb 21, 2017

If there is no way, I try to use the soft SPI……

@Spritetm
Copy link
Member

Spritetm commented Feb 21, 2017

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.

@ghkjgod
Copy link
Author

ghkjgod commented Feb 21, 2017

I'll try it first

@holzachr
Copy link

holzachr commented Feb 21, 2017

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.

@ghkjgod
Copy link
Author

ghkjgod commented Feb 22, 2017

TO holzachr: yes!Yes, I used RTOS, how to solve this problem? I guess it should be for this reason

@ghkjgod
Copy link
Author

ghkjgod commented Feb 22, 2017

I found a way to solve this problem, that is, use the more underlying functions, as used in the emunes project.

@ghkjgod ghkjgod closed this as completed Feb 23, 2017
@wired8
Copy link

wired8 commented Aug 2, 2022

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.

Hey @holzachr would you mind sharing your module?

@espressif-bot espressif-bot added the Status: Opened Issue is new label Aug 2, 2022
@github-actions github-actions bot changed the title HW SPI so slow,How to solve? HW SPI so slow,How to solve? (IDFGH-7973) Aug 2, 2022
@espressif-bot espressif-bot added Resolution: Done Issue is done internally Status: Done Issue is done internally and removed Status: Opened Issue is new labels Aug 3, 2022
@holzachr
Copy link

holzachr commented Aug 5, 2022

Hey @holzachr would you mind sharing your module?

I'm sorry, it looks like the module got last in the sands of time.
Considering how old this thread is, I wouldn't expect it to interact well with the current development environment anyway...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Resolution: Done Issue is done internally Status: Done Issue is done internally
Projects
None yet
Development

No branches or pull requests

6 participants