Can the processor do something else while SPI read or writes are being performed

Status
Not open for further replies.

mrdave45

Member
Hi,

Im working on a fairly complex project and one of the things it does is write to a bunch of SPI dacs and read from an SPI adc.

The write speed of the spi dacs are rated at 20Mhz clk (although before I realised this, I tested them up to 80Mhz and settled on 48Mhz) but the adc spi bus is only rated to about 1.8Mhz.


With a teensy 4.1 running at 600Mhz, it seems that this will be hanging around for between 12.5 cycles for the 48Mhz overclocked DAC (is this a problem for an MCP4822 if the output isn't corrupting?) to ~333 clock cycles for a non overclocked MCP3202 per spi clock.

Im going to be doing around at least 130,000 writes per second. Probably more. For the amount of data being piped across to these devices I seem to be using way too much of the Teensy processing time.



Should I instead use an interrupt and bit bang my own spi using GPIO?

Arguably If my dacs are happy running at 48Mhz, using an interupt is largely pointless because I think it takes 5 cycles at either end of the interrupt to enter and exit, but if i want to slow the SPI clk down to the specified 20Mhz, this is probably worth doing as would lose 10 cycles rather than 30 cycles on the main processor.

or.....

Whilst the spi is being written to or read from, can the processor be doing something else? Is this what the FIFO is for? I think ideally, I would like to pre load a value in to some kind of FIFO/buffer or register, and once an spi write is initiated, some part or peripheral in the processor deals with this by itself whilst the main processor returns to the main loop something more useful rather than waiting for a slow SPI to clock round.

Ideally I would like to call an interrupt every so often to set an address + enable, then whilst my dac is being written to, settling and charging a sample and hold cap, Id like the processor to be calculating new values instead of waiting for the spi write to finish.


Thanks
 
I'm no expert on the Teensy SPI, but maybe I can help with some general information. Yes, the processor can do other things while data is being transferred to/from SPI devices. I recommend reading the LPSPI chapter (48) in the processor reference manual, just to get an idea of the features available. That will help you understand the capabilities of the SPI, and also what is supported by the Teensy 4.x SPI driver. The FIFO supports both DATA and CONTROL fields, so you can put together a sequence of SPI transactions that specify the SPI mode, speed, chip selects, etc, and all of that gets handled automatically. You could configure a set of transfers, start it, and then go do something else until a completion interrupt occurs. There is also DMA. How to best use these features will depend on your design. For example, how many SPI dacs are there in "a bunch"? The MCP4822 has only one CS pin, so each one requires a separate CS pin. I'm pretty sure the SPI supports 4 CS pins, and therefore you can only control 4 of those DACs with a given FIFO setup. The 4822 is double-buffered and has a /LDAC signal to synchronize the outputs of both channels on each DAC. Do you need that? Do you need to synchronize the outputs across all of the DACs? As you can see, there is a lot to think about, so try to clarify your requirements.
 
I'm no expert on the Teensy SPI, but maybe I can help with some general information. Yes, the processor can do other things while data is being transferred to/from SPI devices. I recommend reading the LPSPI chapter (48) in the processor reference manual, just to get an idea of the features available. That will help you understand the capabilities of the SPI, and also what is supported by the Teensy 4.x SPI driver. The FIFO supports both DATA and CONTROL fields, so you can put together a sequence of SPI transactions that specify the SPI mode, speed, chip selects, etc, and all of that gets handled automatically. You could configure a set of transfers, start it, and then go do something else until a completion interrupt occurs. There is also DMA. How to best use these features will depend on your design. For example, how many SPI dacs are there in "a bunch"? The MCP4822 has only one CS pin, so each one requires a separate CS pin. I'm pretty sure the SPI supports 4 CS pins, and therefore you can only control 4 of those DACs with a given FIFO setup. The 4822 is double-buffered and has a /LDAC signal to synchronize the outputs of both channels on each DAC. Do you need that? Do you need to synchronize the outputs across all of the DACs? As you can see, there is a lot to think about, so try to clarify your requirements.

Hi,

That largely answers my question.

I am using 8 dacs going into multiplexors for CVs for a large synthesizer system. /LDAC sync is not needed.

I am aware of the !CS limits and so am using an addressing and buffering system using various logic devices like active low 3 to 8 line decoders etc so each dac does have a direct !CS signal. Im also using the enable pin on active high AHCT (good fan out and high speed) 3 to 8 line decoders to give separate mosi and clk signals which will have to work across shortish idc cables. Im also trying to limit sending the same clock signals down multiple lines at the same time to try to cut down on possible radiated noise hence the addresable 3 to 8 method rather than a bunch of parallel buffers. Essentially each device thinks it has it own dedicated MOSI, CLK and !CS line.


I have built a single channel (one 2 channel dac multiplexed out to 32 sample and hold circuits for control voltages) prototype of the control board and this worked. This also had addressing and cs lines, mux enables etc to go to the other 8x 2 channel dacs which i examined with a logic anaylser and tried the dac on various output channels. The prototype used shift registers to do the !CS lines, this new version uses 3 to 8 line decoders instead as I need to drive these lines via short idc connectors so Ive kept fan out to a minimum.

Ive been using the time taken to cycle round the different CS lines to give the sample and hold caps time to charge etc, but i think i can save a lot of wasted processor time by investigating this LPSPI chapter.

Its also good to know that completion interrupts are a thing. Thats another thing i need to look into more closely.


Many thanks
 
Yes the Teensy can do other things while it reads from and/or writes to SPI.

The issue is more of how to do so software wise.

That is if you call something like: x = SPI.transfer(y);
It will wait until the SPI completes the full transfer, such that it can return x...
this is true of some of the SPI transfers that use buffers, like: SPI.transfer(txBuffer, rxBuffer, count);
it will wait until the full thing transfers before returns, even if you pass in NULL for rxBuffer.

However there are ways to do this using DMA to do the transfers. We do have one method in the SPI library:
bool transfer(const void *txBuffer, void *rxBuffer, size_t count, EventResponderRef event_responder);
Which will start up a dma transfer and will trigger an event responder object, when it is done.


There are several other libraries that setup to do DMA operations on SPI, including some of the different display libraries, including my ili9341_t3n.
This will also incorporated into some other like the st7735_t3 library and some others.

You can also roll your own code to do some work while doing spi transfers without dma, as for example the processor has 16 word buffers for RX and TX, so your code can for example push some data into the FIFO and maybe compute the next thing to push onto the fifo while the queue is still not empty...
 
Thank you KurtE,
I think this is going to take a little while to digest, but that's pointed me exactly where i need to be.
Regards.
 
From another page on this forum havinf searched about spi cs pins that is largely the same topic as here, but i didnt want to hijack someone elses thread....

it is different on T4.x. Their output FIFO is handled by two different registers. You put stuff to output on the queue using TDR register, which allows you to transfer 1-32 bits of data per transfer. And there is another register TCR which allows you to change things like Word size and which CS pin to assert. And in this case only one CS pin can be asserted.



Say Im using

one master teensy (4.1), plus 2 slave teensy's (4.1) and 1 slave ADC all connected to:

pin 11 mosi
pin 12 miso
pin 13 SCK

then
pin 10 CS to adc
pin 37 CS to slave teensy 1
pin 26 CS to slave teensy 2.

So I would put my data in TDR
then set the relevant bit of TCR to (for example) pin 37 to work with slave teensy 1
hit the go button (whatever line of code that is)

Then that will communicate with said device and will do that autonomously whilst the master teensy can go and do something more useful instead.

I can also change the sck rate at will to 1.8mhz for my super cheap slow ADC, up to 250mhz or whatever it is for the teensy fastest SPI (cable arrangement permitting).

And for handshaking between the teensy's, I take it i should take the approach of asking the teensy how much data it wants to send (i believe i2c works like this?) then clock x bytes out of it, rather than mess around with handshake pins.

Sorry if this has clearly been answered, Im a bit new to a lot of these terms (tdr, tcr) and my motherboard design is pretty much maxed out pin wise so i need to make sure Ive got the hardware connections correct and worry about the finer details of the software once we've got this new version made.

Thanks
 
I made a daisy-chainable SPI slave library, you can use it either separate or same CS, and you can send whatever arrays you want from master to slave or slave to master

you could do it yourself though the slave registers can be a pain to work with, and then the handshake protocol afterwards. then theres also the issue where the master requires the DSE bits set to 3 for the slave to work properly. if you want to go look at the library it will help you figure out whats involved to make things work if you want to custom make it your way

of course if you have them on a separate bus on the master and incorporate teensy threads, you could talk to all of them at same time without bus conflicts provided you're not sharing the commands for the spi or data between threads, unless you use mutexes.
 
Last edited:
I made a daisy-chainable SPI slave library, you can use it either separate or same CS, and you can send whatever arrays you want from master to slave or slave to master

you could do it yourself though the slave registers can be a pain to work with, and then the handshake protocol afterwards. then theres also the issue where the master requires the DSE bits set to 3 for the slave to work properly. if you want to go look at the library it will help you figure out whats involved to make things work if you want to custom make it your way

of course if you have them on a separate bus on the master and incorporate teensy threads, you could talk to all of them at same time without bus conflicts provided you're not sharing the commands for the spi between threads, unless you use mutexes.

Hi that sounds great. Where would i find this library?
 
Status
Not open for further replies.
Back
Top