SPI.dmaSendAsync() has been added to SPI

Information on the latest releases
User avatar
RogerClark
Posts: 7693
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

SPI.dmaSendAsync() has been added to SPI

Post by RogerClark » Mon Jun 12, 2017 7:24 am

This is a bit of a hack, but I needed it for my WS2812B (aka Neopixel) library, so I've added a new function to SPI which does a non-blocking transfer

The function is just a copy of dmaSend() with the blocking code, moved to the start of the function, so that if dmaSendAsync is called while a transfer is still happening, the code will block until the previous transfer is complete before clearing down and re-establishing the next DMA transfer

I've only tested it with my WS2812B library, but it seems to work OK

In the longer term, we may be able to add a callback function e.g. as an argument, and I know @victor_pv has been investigating non-blocking SPI DMA transfers, but I can't remember how far he got with this or whether it is practical at all to do it.

Note, if you intend to use this function, you will need to use a double buffering strategy.

racemaniac
Posts: 699
Joined: Sat Nov 07, 2015 9:09 am

Re: SPI.dmaSendAsync() has been added to SPI

Post by racemaniac » Mon Jun 12, 2017 8:05 am

really great to see this finally being part of the core library :).
i may still have some libmaple based spi DMA code that also has the end of transfer interrupt.
(although for sending it's VERY tricky :s. i think i ended up only using spi transfers because a transfer finishes when the dma receives the last byte (and then the SPI port is also done. When only sending however, the DMA ends when the last byte is sent to the spi port, but then the port still has to transfer it. So if you disable the SPI port when the DMA is done.... you'll interrupt the transmission of the last byte....)

stevestrong
Posts: 2073
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany
Contact:

Re: SPI.dmaSendAsync() has been added to SPI

Post by stevestrong » Mon Jun 12, 2017 8:46 am

Roger, did you test the performance comparative to with and without buffered DMA?

I think I made a lot of tests regarding buffered SPI DMA, see here: viewtopic.php?f=9&t=1654&start=40#p21696.
My conclusion was that there is only one use case in which is really worth to consider buffered DMA. This is the filled rectangle test case for Adafruit TFT lib.
If your use case is similar to this (I haven't had a look at the WS2812B driver), then it make sense. Otherwise is questionable, at least from the speed performance point of view.

racemaniac
Posts: 699
Joined: Sat Nov 07, 2015 9:09 am

Re: SPI.dmaSendAsync() has been added to SPI

Post by racemaniac » Mon Jun 12, 2017 9:43 am

stevestrong wrote:Roger, did you test the performance comparative to with and without buffered DMA?

I think I made a lot of tests regarding buffered SPI DMA, see here: viewtopic.php?f=9&t=1654&start=40#p21696.
My conclusion was that there is only one use case in which is really worth to consider buffered DMA. This is the filled rectangle test case for Adafruit TFT lib.
If your use case is similar to this (I haven't had a look at the WS2812B driver), then it make sense. Otherwise is questionable, at least from the speed performance point of view.
in this case you're sending kilobytes of data via the SPI port in 1 call at a relatively low rate. So waiting for it to complete is quite a waste of time i'd think.

User avatar
RogerClark
Posts: 7693
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: SPI.dmaSendAsync() has been added to SPI

Post by RogerClark » Mon Jun 12, 2017 9:51 am

Steve

The data rate for the WS2812B is fairly slow. The spec is 800kHz, (1.25uS per bit), 24 bits per LED = 30uS per LED.
I have to run SPI1 at DIV32 to generate the correct period for the mark/space ratio of 1:2.

If anyone wants to drive a 5m string of 60 LEDs per m, thats 300 LEDS, so the transfer time would be 9mS

The spec says that the Reset period, between transfers needs to be 50uS, - which is a lot of processor cycles to prepare the next set of data, but in reality , as blogged about by several people ( https://wp.josh.com/2014/05/13/ws2812-n ... know-them/ ) , the Reset period, only needs to be 6uS, which leaves a lot less time to prepare the next load of pixels.

But I generally agree it may not be that necessary to send asynchronously

racemaniac
Posts: 699
Joined: Sat Nov 07, 2015 9:09 am

Re: SPI.dmaSendAsync() has been added to SPI

Post by racemaniac » Mon Jun 12, 2017 11:00 am

I think it's nice to have the option. If your goal is just to drive neopixels, then it doesn't matter that much. If your project wants to nicely animate a large chain (and thus at 30+ fps), and wants to do other work at the same time, having the async option could be very useful :). (especially if you have something else that also requires critical timings)

User avatar
RogerClark
Posts: 7693
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: SPI.dmaSendAsync() has been added to SPI

Post by RogerClark » Mon Jun 12, 2017 11:09 am

I'm going to buy some 300 LED (5m) strips and probably make one of them into a small dot matrix display e.g. 16 x 16 pixel

And like you say. Its nice to have the option to do this, and would be even better if we could assign a completion callback if we wanted one

stevestrong
Posts: 2073
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany
Contact:

Re: SPI.dmaSendAsync() has been added to SPI

Post by stevestrong » Mon Jun 12, 2017 11:12 am

I totally agree with, guys, that in this use case this makes sense.
I was just trying to point out that this is not always the case. Just to think that during DMA transfer the CPU has time to do other tasks, is not always a guarantee for a better performance than without DMA, due to the overhead caused by the DMA routine and due to the fact that the processor is slowed down by the DMA (they are both accessing the memory bus which must be arbitrated).

Btw, Roger, assuming that you are aware of this, the code includes a buffer copy part which could be avoided by further optimization.
My code (under the link above) includes some examples how to use callback and IRQ controlled end of transfer.

User avatar
RogerClark
Posts: 7693
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: SPI.dmaSendAsync() has been added to SPI

Post by RogerClark » Mon Jun 12, 2017 11:27 am

@stevestrong

Do you mean the buffer copy I do after the dmaSendAsync ?

I didnt originally copy the data, but the problem is the existing examples rely on previous data, so double buffering screws this up unless I copy the buffer each time

But perhaps you mean something else ?

Re: Memory bus arbitration

Thats a good point. I'd not considered.

DMA seems to take priority as I've not noticed the SPI pulse-train getting messed up.


One other thing...

In the case of the WS2812B, to bit-bang the data, the interrupts need to be turned off.

In the existing bit-banged code I have, the interrupts are turned off for the entire duration of the data send process.
But this seems to break USB Serial as its interrupt is not being handled. It may effect other things that use interrupts (systick ???).

So SPI DMA is possibly the only way to stay within the WS2812B data transmission spec and also maintain USB serial, especially on long chains of LEDs.

In reality, the WS2812B does not care if there is a short break between each pixel that is transmitted, so it may be possible to briefly enable interrupts between pixels. But I don't know how long the USB ISR code takes to run (I presume there is code doing something)??

racemaniac
Posts: 699
Joined: Sat Nov 07, 2015 9:09 am

Re: SPI.dmaSendAsync() has been added to SPI

Post by racemaniac » Mon Jun 12, 2017 11:38 am

RogerClark wrote:Re: Memory bus arbitration

Thats a good point. I'd not considered.

DMA seems to take priority as I've not noticed the SPI pulse-train getting messed up.
I should really find some time to measure this. The memory bus has excess capacity, so a slower dma like the one for the neopixels shouldn't affect the cpu that much, but unless we take the effort to measure how dma affects the processor, we'll never know how useful (or not useful) it is >_<
it's indeed not completely free the async dma, but neopixels are a very good case where it's probably very useful :)

Post Reply