FYI about DMA issues on esp32

FYI, I modified the python xpt2046 driver to perform a “read” (get X Y coords of a click) using a single SPI transaction and using the “16 cycle overlapped” mode. For this I had to switch to “full duplex” SPI transactions. Well, that didn’t go so well…

After more than a day of banging my head against the wall I discovered that the problem is a bug in esp-idf. Specifically, the ili9341 display uses half duplex transactions and that causes all full duplex transactions on the same bus to break: they run correctly looking at the pins but the data sent by the slave doesn’t show up in the rx buffer (under some circumstances the first 4 bytes do show up). Note that the esp-idf docs have some warning that half duplex and rx has bugs…

After discovering all this in test cases I switched the display driver to run full-duplex transactions (it’s just a matter of commenting out the "flags": esp.SPI_DEVICE.HALFDUPLEX line) and now everything works. Argh!

It might be worth making this change in the display driver so others don’t hit similar bugs… I don’t see any downside.

Hi @tve!
Could you explain the motivation for this change? What was the problem you were trying to solve?

There is a good reason it’s configured as half-duplex.
See: https://github.com/lvgl/lv_binding_micropython/issues/69#issuecomment-586475311

In a nutshell, Full-duplex transactions are not compatible with the dummy bit workaround and limit SPI speed to 26MHz.
On the other hand, half-duplex with the dummy bit workaround allow speeds up to 80MHz. I usually use the display driver at 40MHz.

It’s possible, however, to add another parameter to ili9341 constructor to configure it as full-duplex when needed, if it helps to solve some problems and when the SPI speed is less important.
If you want to help, we always welcome PRs!

The problem I was trying to solve was that it wasn’t working at all. I couldn’t tell whether it was a software or hardware issue so I read the datasheet, looked at other drivers, and rewrote. In the end the real problem turned out to be that two pins on the touch controller chip were shorted and the flex cable to the touch panel was also improperly soldered. I suppose the module I got is a reject from some manufacturing run… Sigh.

In the end, the new scheme has a bunch of benefits:

  • it runs a single transaction to read x, y, and pressure, which is faster
  • it provides extended settling time as described in the original burr brown app note, and I see experimentally that it helps
  • it’s less code and simpler code

Thanks for pointing this out. I’ve read the dummy bit workaround 4 times now and I still don’t understand what that really does. I understand the problem, just not the workaround…
But how is this relevant for the display given that nothing is being read? Is it that esp-idf doesn’t allow you to configure the clock to 40mhz?

The docs say:

Dummy bit workaround : Dummy clocks, during which the Host does not read data, can be inserted before the read phase begins. The Device still sees the dummy clocks and sends out data, but the Host does not read until the read phase comes. This compensates for the lack of the MISO setup time required by the Host and allows the Host to do reading at a higher frequency.

So it seems that ESP32 has some timing issue, and esp-idf automatically solves this by either adding the dummy bit in half duplex mode, or by limiting the SPI frequency.

They explicitly say that they limit frequency on full duplex mode:

Full-duplex transactions are not compatible with the dummy bit workaround , hence the frequency is limited. See dummy bit speed-up workaround.

Since the ili9341 only writes, as you mentioned, another option is to try enabling the SPI_DEVICE_NO_DUMMY option in full duplex mode:

In full-duplex mode, however, the hardware cannot use dummy bits, so there is no way to prevent data being read from getting corrupted. Set this flag to confirm that you’re going to work with output only, or read without dummy bits at your own risk.

I didn’t try it though.

Since the ili9341 only writes, as you mentioned, another option is to try enabling the SPI_DEVICE_NO_DUMMY option in full duplex mode:

Looking at the code https://github.com/espressif/esp-idf/blob/master/components/soc/src/hal/spi_hal.c#L65-L70:

Specify SPI_DEVICE_NO_DUMMY to ignore this checking.

With the following initialization I do see a 40Mhz clock on my scope:

disp = ili9341(spihost=2, miso=19, mosi=23, clk=18,
    cs=12, dc=14, rst=13, power=-1, backlight=-1,
    mhz=40, factor=16, hybrid=True)

I did not add SPI_DEVICE_NO_DUMMY, but I am using the iomux pins. ESP-IDF 4.0.1.
Go figure…

I’ve opened an issue to track this: https://github.com/lvgl/lv_binding_micropython/issues/80
It could be useful to configure the SPI drivers to full duplex with SPI_DEVICE_NO_DUMMY enabled, if this solves problems with other devices on the same SPI bus.

@tve - Do you see value in pushing your changes to xpt2046 into lv_binding_micropython repo?

@tve I’ve added an option to configure ili9341 to full duplex.
Where can I find your version of xpt2046 with “16 cycle overlapped” full duplex mode?

Thank you for the link @tve !

I wanted to make a quick test comparing your driver to mine.
I’ve set ili9341 driver to full duplex and tried your version of xpt2046 - it doesn’t work for me.
Initialization of both ili9341 and xpt2046 is completed, but something hangs afterwards, no display and no Micropython REPL.
I haven’t really tried to debug it, maybe if I find some time I’ll try later.

Is this a known bug in esp-idf? Could you point me to the bug report?
Or is this something you discovered when working with ESP32 SPI?

I found out that running my version of xpt2046 (half duplex) with ili9341 full duplex works for me, so for some reason I’m not experiencing this bug. (esp-idf v4.0.1)