Closed DatanoiseTV closed 5 months ago
@earlephilhower If you want to test it, you can use the PicoADK board I've sent you some time ago.
This is pretty low prio for me, to be honest. If it's important for you, I think it's doable without too many tears.
The AudioBufferManager
already handles most of DMA work (covers PIO I2S in and out, internal ADC reads, and internal PWM output) so this is more on figuring out how to get SPI repeated xactions and designing an API that makes sense for the common use cases. The RP2040 datasheet and Pico-SDK docs would be the best guides to get you going on the SPI HW config.
@earlephilhower For the PicoADK it would be pretty important. I've tried my luck without success so far. Problem is also, that the TX Channel needs to send following data to cycle between adc ch0-ch7 for example:
volatile const uint16_t txbuf[TEST_SIZE] __attribute__((aligned(TEST_SIZE * sizeof(uint16_t)))) = {
0 << 11,
1 << 11,
2 << 11,
3 << 11,
4 << 11,
5 << 11,
6 << 11,
7 << 11
};
Spitballing here, but I think you'd set up an ABM INPUT stream from the SPI read FIFO, and then externally set up a recurrent DMA loop off a timer of 1/fs * sizeof(txbuf)
that reads txbuff over and over and writes to the SPI write FIFO. You'll get samples from each channel spread over time, not instantaneously, but other than a constant time offset all will come in at fs.
In this case I'd probably make a special-purpose SPI setup since this looks to have rather different requirements than the general purpose one...
If you would find some time for this, I would be very grateful! I am not sure I understood your last message.
+1 for non-blocking SPI communication! This gets more important as the peripheral's max tx rate gets more slow. My current project spends over 20% of its main loop blocked on an SPI transfer of < 20 bytes. The transfer speed itself wouldn't be a problem if I could do something else with those cycles.
I tried hacking out some code for this today and ran into a roadblock.
While getting DMA set up to feed/eat the SPI data is straightforward enough, actually knowing when the SPI transaction is completed is not doable via IRQ. There is no IRQ for SPI done, there is no IRQ even for SPI FIFO empty.
So, while it would be possible to make a SPI::transferDMA(.,..)
the user app would need to busy poll the SPI device to determine if it's completed or not. There can't be a simple callback on completion, as I imagine you'd expect. You'd have to do something like:
SPI.transferDMA(...);
<do some stuff that doesn't need the SPI send or recv data>
while (SPI.busy()) { /* busy wait */ }
<do some stuff dependent on SPI data being sent/received, like start a new SPI transaction>
Given that, I'm not sure it's so useful. Thoughts?
You know way more about this than I do :D, but: What's SSPTXINTR? "The transmit interrupt is asserted when there are four or fewer valid entries in the transmit FIFO." Is that like a "FIFO half empty" thing? Probably a stupid idea: SPI is a fixed clock, right? So could you calculate how long it will take to transmit/read X bytes and set a timer based on that as a "SPI DMA done interrupt"?😆
Would DMA SPI be applicable to reading (part of) a file from SD card? If yes, then I think I have an application where DMA would improve performance but a "spi done" interrupt would not be needed. void loop(){
As long as steps 2-4 always take longer than the SPI transfer, a "SPI done" interrupt would be useless in this case. If steps 2-4 are faster than the SPI transfer, then a "SPI done" interrupt would be nicer than busy waiting. But DMA would still increase performance.
What's SSPTXINTR? "The transmit interrupt is asserted when there are four or fewer valid entries in the transmit FIFO." Is that like a "FIFO half empty" thing?
Yeah, but not a precise half-empty one. Given the "or fewer" part I'm not sure what the designer expected the code to do.
Probably a stupid idea: SPI is a fixed clock, right? So could you calculate how long it will take to transmit/read X bytes and set a timer based on that as a "SPI DMA done interrupt"?
Actually, that seems like a possibility. No clock stretching allowed like in I2C so you can know timing. I'd need to think about the granularity of alarms, though. Might normally be too small or something...
Would DMA SPI be applicable to reading (part of) a file from SD card? (example)
The SPI is also used to read things like FAT tables and directory entries inside the SDFat code so w/o massive rewriting internally I don't think it could work. Your data reads are a very special case, I think. Inside SDFat there is no differentiation between what purpose the SPI access is being performed.
Couldn't the SPI transaction / reading be done using the DREQ from the timer? I've seen some examples where SPI transfers were done at 44100Hz rate, but can't find it anymore.
It's not quite that simple when talking to SD cards. Even if you didn't have a filesystem and used it like a tape you'd still need to initiate each chunk of sector reads and wait for ready. So you'd need to buffer anyway, and in that case you'd probably just want to run as fast as possible and not limit its speed.
Add a filesystem on top and you could end up needing to read a different address every 512 bytes (plus maybe peeking at the FAT if you don't cache it because it's too big).
It's not quite that simple when talking to SD cards. Even if you didn't have a filesystem and used it like a tape you'd still need to initiate each chunk of sector reads and wait for ready. So you'd need to buffer anyway, and in that case you'd probably just want to run as fast as possible and not limit its speed.
Add a filesystem on top and you could end up needing to read a different address every 512 bytes (plus maybe peeking at the FAT if you don't cache it because it's too big).
Ah, I see. For an SPI ADC / DAC that would probably make sense, though.
For some applications like controlling a SPI display, DAC or ADC DMA would make sense to save some CPU cycles. An example is my PicoADK Board, which has an on-board SPI ADC128S102 8ch 1msps ADC. With DMA, the SPI ADC could be free-running or sampled at a certain interval without involving the CPU.
If this would be done using the HW SPI peripheral, the CS pin would be most likely limited to the CS pins listed for the corresponding CS options in the pin matrix.
An alternative approach would be to use PIO (there is a reference implementation for SPI). This would still allow for high clock rates, but more flexibility to the choice of the SPI pins.