Pause SPI clock momentarily while using DMA

akasaka_spk
Posts: 15
Joined: Tue Oct 08, 2024 9:11 am
Location: Sapporo, Japan

Pause SPI clock momentarily while using DMA

Postby akasaka_spk » Mon Nov 04, 2024 3:53 am

I'm trying to use the ESP32's SPI peripheral to drive a chain of LED matrices dynamically.

The LED matrices have a shift register for the vertical and horizontal control lines, so the cycle is basically:
1. Shift out (row length/2) bits on QIO0 and QIO1 with the bitmap data, while also in QIO2 setting the bits at positions (bit_num % row_number) to 1 to enable the correct row.
2. Stop the clock and momentarily change the LATCH pin state from 1 to 0 then back.
3. Continue with the next row

What I currently do on every frame (at roughly 60 Hz) is queue 17 transactions by DMA (one for each row + one dummy so that the bottom row isn't brighter than the rest due to waiting for the next frame to come). Then in the `post_cb` ISR I do the LATCH change.

The final code is here inside the project: https://github.com/vladkorotnev/plasma- ... i_k875.cpp

However, because of the many other tasks running on the ESP, the timing of this process drifts significantly. Eventually some rows start blinking brighter than the others at random because there is some task that has to be taken care of between the post_cb happens and the next transaction starts.

So I thought to use low-level SPI and circular linked lists similar to that one VGA output project, to never have to bother with the callback and enqueueing the transactions again. For that I would wire the LATCH pin to the currently unused QIO3, and clear that bit in the data array between frames when I need to latch the row.

However even before implementing the circular linked list solution, just trying this approach with the normal SPI and the whole screen in a single transaction, resulted in "noise" next to the active pixels. You can see those are not perfect diagonal lines but have some "jitter".
20241028_225545.jpg
20241028_225545.jpg (941.74 KiB) Viewed 1744 times
This seems to be because of the clock signal being still active while the LATCH change is happening with this approach — because the shift register continues to shift even though there is no data being fed and latch is active.

So my question is, can I pause the clock of the SPI easily for just one bus cycle?

P.S. Now that I think of it, maybe a good solution would be to use the clock-on-CS configuration, and send that one single bus cycle to a different dummy device, so that the shift register doesn't see the clock signal. But I'm not sure if it's possible to do multiple devices in a single transaction, neither whether that is doable with the clock-on-CS flag set.

MicroController
Posts: 1733
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Pause SPI clock momentarily while using DMA

Postby MicroController » Mon Nov 04, 2024 7:41 am

Can you use QIO3 for the latch signal?
Or the CS signal?

aliarifat794
Posts: 200
Joined: Sun Jun 23, 2024 6:18 pm

Re: Pause SPI clock momentarily while using DMA

Postby aliarifat794 » Mon Nov 04, 2024 7:43 am

You can add a dummy DMA descriptor at the end of each frame with no output data to allow a pause in the cycle.

akasaka_spk
Posts: 15
Joined: Tue Oct 08, 2024 9:11 am
Location: Sapporo, Japan

Re: Pause SPI clock momentarily while using DMA

Postby akasaka_spk » Mon Nov 04, 2024 8:51 am

MicroController
The picture above with the offset and noise is actually from when using QIO3 for the latch signal.
As for the CS... good catch, I don't know why I didn't do that — judging by the `SPI_DEVICE_POSITIVE_CS` flag already in the configuration I tried it at some point and then for some reason removed it. I've put it back and now I don't need the `post_cb` anymore it seems, everything works as it was before — the flicker is still there, but less noticeable.

aliarifat794
Given the above changes now I wonder, would adding a dummy DMA descriptor cause the CS line to disengage while the pause is ongoing?

Also is there some easier example about how to set up low level circular DMA for SPI? The VGA code is somewhat hard to follow for someone with zero experience in programming on such a low level for the ESP.

akasaka_spk
Posts: 15
Joined: Tue Oct 08, 2024 9:11 am
Location: Sapporo, Japan

Re: Pause SPI clock momentarily while using DMA

Postby akasaka_spk » Tue Nov 05, 2024 9:38 am

OK, so I have tried to copy the VGA example and adapt it for QIO like this:

https://github.com/vladkorotnev/plasma- ... #L149-L342

However, I don't see any activity on the display bus now whatsoever.
Is there something that has changed in the register assignment since the VGA example was posted?

akasaka_spk
Posts: 15
Joined: Tue Oct 08, 2024 9:11 am
Location: Sapporo, Japan

Re: Pause SPI clock momentarily while using DMA

Postby akasaka_spk » Wed Nov 06, 2024 9:16 am

After changing it to this, it can finally do the DMA without taking up CPU:

https://github.com/vladkorotnev/plasma- ... i_k875.cpp

However, the CS line stays idle, so I have to generate my own latch signal (not very successfully, the picture slides across like an old broken TV).

Is there some way to mark the virtual end of transaction in the lldesc_s so that it bumps the CS line for a bit?

akasaka_spk
Posts: 15
Joined: Tue Oct 08, 2024 9:11 am
Location: Sapporo, Japan

[SOLVED] Pause SPI clock momentarily while using DMA

Postby akasaka_spk » Thu Nov 07, 2024 10:00 am

I have managed to solve it by cleverly using the already-sacrificed GPIO pin, putting the LATCH signal in the data as suggested above, and the MCPWM peripheral.

Basically I can set the MCPWM module to produce a 100% duty cycle, thus a constant logic HIGH.

Then it has emergency stop inputs, which I can configure to send the output LOW when any of those go LOW.

So I use the pin mux to connect the MCPWM inputs to the SPI outputs, and then connect the MCPWM output to where I need my clock signal.

And presto, aside from some flicker due to concurrent writing while DMA is reading, this is fully working dynamic refresh of a shift-register based display without making the CPU busy. I think I've read someone on this forum saying the original ESP32 is too slow for being a display driver without an external controller and with letting the CPU do something useful aside from driving the display. Guess I've proved them wrong! :-)

The absolutely deranged source code with extensive comments is here: https://github.com/vladkorotnev/plasma- ... #L233-L320

Hope you find this exercise amusing or interesting. On my side, I'll go get a beer and question what am I doing with my life... :P

MicroController
Posts: 1733
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Pause SPI clock momentarily while using DMA

Postby MicroController » Thu Nov 07, 2024 9:40 pm

Code: Select all

// ... I'm too lazy and cheap to use a whole package for just 2 transistors worth of stuff.
// And I'm even lazier to use just 2 transistors, because ...
// Thus we have just created an AND gate out of the MCPWM unit!
Man, I'm totally in on that :) I really like this way of forcing what's already there to do what you want and no one has thought of before.

Who is online

Users browsing this forum: No registered users and 59 guests