SPI DMA gotchas

vyo2003
Posts: 19
Joined: Fri Dec 22, 2017 8:36 am

SPI DMA gotchas

Postby vyo2003 » Fri Apr 05, 2019 9:59 pm

I'm trying to port u8g2 library to ESP-32 while keeping interface the same. Current part is SPI driver.

I form a buffer from incoming bytes, then send it using DMA but something seems off and display show some messed up picture (but it is still possible to say that there is one).

I'm actually lost with possible mistakes. Are there any specific situations where just sending an array of bytes does not work as intended? While there are full bytes, they don't always form 32bit blocks. Is there anything special about such non-32bit complete blocks? What are other ways I can screw up SPI a little but not too much?

Thank you.

vyo2003
Posts: 19
Joined: Fri Dec 22, 2017 8:36 am

Re: SPI DMA gotchas

Postby vyo2003 » Sat Apr 06, 2019 10:57 am

Here's my problematic code:

Code: Select all

static const spi_host_device_t u8x8_spibuses[2] = {HSPI_HOST, VSPI_HOST};

static uint8_t u8x8_byte_espidf_hw_spi_universal(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr, uint8_t host)
// It can work with any of two SPIs
	{
	u8x8_device_info* info = u8x8->user_ptr;
	switch (msg) {
			case U8X8_MSG_BYTE_SEND:
				;

				// We add stuff into buffer and then send it in one transaction
				//if (info->spi.bufsiz != 0)
				//	hexDump("Old buf", info->spi.buffer, info->spi.bufsiz);
				size_t oldsize = info->spi.bufsiz;
				info->spi.bufsiz += arg_int;
				//hexDump("Appending", arg_ptr, arg_int);
				info->spi.buffer = heap_caps_realloc(info->spi.buffer, info->spi.bufsiz, MALLOC_CAP_DMA);
				memcpy(info->spi.buffer + oldsize, arg_ptr, arg_int);
				//hexDump("New buf", info->spi.buffer, info->spi.bufsiz);

				break;
			case U8X8_MSG_BYTE_INIT:
				;
				info->type = SPI;
				info->spi.device = NULL;
				info->spi.buffer = NULL;
				info->spi.bufsiz = 0;
				spi_bus_config_t busconf = {};
				busconf.mosi_io_num = u8x8->pins[U8X8_PIN_SPI_DATA];
				busconf.miso_io_num = -1;
				busconf.sclk_io_num = u8x8->pins[U8X8_PIN_SPI_CLOCK];
				busconf.quadwp_io_num = -1;
				busconf.quadhd_io_num = -1;
				busconf.max_transfer_sz = 0; // FIXME: add it into config
				busconf.flags = 0; // FIXME: add stuff here?
				if (u8x8->bus_clock == 0) {	/* issue 769 */
						u8x8->bus_clock = u8x8->display_info->sck_clock_hz;
						}
				/* disable chipselect */
				//u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);

				spi_bus_initialize(u8x8_spibuses[host], &busconf, host + 1);    // NOTE: user can set up it manually

				spi_device_interface_config_t conf = {};
				conf.command_bits = 0;
				conf.address_bits = 0;
				conf.dummy_bits = 0; // FIXME: Hmmm, may cause problems?
				conf.mode = u8x8->display_info->spi_mode;
				conf.duty_cycle_pos = 0; // I'm not sure
				conf.clock_speed_hz = u8x8->bus_clock;
				conf.input_delay_ns = 0;
				conf.spics_io_num = u8x8->pins[U8X8_PIN_CS];
				conf.flags = SPI_DEVICE_NO_DUMMY;
				if (u8x8->display_info->chip_enable_level) {
						conf.flags |= SPI_DEVICE_POSITIVE_CS;
						}
				conf.queue_size = 1; // I only use polling transactions, but ok
				ESP_ERROR_CHECK(spi_bus_add_device(u8x8_spibuses[host], &conf, & (info->spi.device)));         // FIXME: remove in destructor

				break;

			case U8X8_MSG_BYTE_SET_DC:
				u8x8_gpio_SetDC(u8x8, arg_int);
				break;

			case U8X8_MSG_BYTE_START_TRANSFER:

				// Nothing to do here
				break;

			case U8X8_MSG_BYTE_END_TRANSFER:
				;
				// Docs says that it's better to have 32-bit aligned memory
				// Doing it here should make stuff faster
				info->spi.buffer = heap_caps_realloc(info->spi.buffer, info->spi.bufsiz, MALLOC_CAP_DMA | MALLOC_CAP_32BIT);
				/*
				size_t incomplete_len = info->spi.bufsiz % 4U;
				if (incomplete_len > 0) {
					uint32_t* lastbyte = (uint32_t*)(info->spi.buffer - incomplete_len);
					*lastbyte = SPI_SWAP_DATA_TX(*lastbyte, incomplete_len*8);
					ESP_LOGI("u8x8", "Hi there! Size: %u, mistake: %u", info->spi.bufsiz, incomplete_len);
				}
				*/
				spi_transaction_t transaction = {};
				transaction.flags = 0;
				transaction.length = (info->spi.bufsiz) * 8;   // Bits
				transaction.tx_buffer = info->spi.buffer;
				transaction.rx_buffer = NULL; // U8g2 don't care about feedback, bad guys!
				spi_device_polling_transmit(info->spi.device, &transaction);
				heap_caps_free(info->spi.buffer);
				info->spi.buffer = NULL;
				info->spi.bufsiz = 0;

				break;
			default:
				return 0;
			}

	return 1;
	}
And here is how il looks like whe I draw full-screen white box (each change is complete frame draw): https://youtu.be/Vzd91BQ8rZw
UPD: longer video https://youtu.be/K7tfP_Z6994

vyo2003
Posts: 19
Joined: Fri Dec 22, 2017 8:36 am

Re: SPI DMA gotchas

Postby vyo2003 » Sat Apr 06, 2019 2:02 pm

Well, problem lies outside of SPI: in DC line. Turned out that I have to flush data before every DC change.

Everything is working fine now :D. If somebody is wondering, you can find working code in https://github.com/v1993/u8g2/blob/mast ... /U8x8lib.c.

Who is online

Users browsing this forum: No registered users and 245 guests