SPI in interrupt with display

Status
Not open for further replies.

bout100

Member
Hi all, I'm trying to get my head around using SPI in/with interrupts, some background on what I'm trying to do:

I have an SPI DAC that I want to update at a set sample rate, updating at audio rate (around 45KHz) is my end goal. I'm using intervalTimer to update the DAC periodically and this is working fine on it's own, but the issue is that I'm also using a TFT display on the same SPI bus. I can get the two to function together by using the usingInterrupts() function of the SPI library, but any lengthy display update is blocking the update of the DAC and causing glitches in its output.

Does anyone know if it's possible to set the priority of interrupts, so that that an SPI transaction called from the DAC's intervalTimer's ISR can interrupt a display update SPI transaction? My feeling is that even if it were, it might not be possible to cancel an SPI transation, otherwise incomplete data would be latched in, and even if it were, a lengthy display update would never have enough time to complete fully before the next 45KHz DAC update came along?

Anyone's insight to this issue would be greatly appreciated!
 
I can get the two to function together by using the usingInterrupts() function of the SPI library, but any lengthy display update is blocking the update of the DAC and causing glitches in its output.

Yeah, that's exactly what it's designed to do.

The 2 solutions are to edit the display code to use shorter transactions, or to use a separate SPI bus (really only an option on Teensy 3.5 & 3.6).
 
Yeah, that's exactly what it's designed to do.

The 2 solutions are to edit the display code to use shorter transactions, or to use a separate SPI bus (really only an option on Teensy 3.5 & 3.6).

@Paul, TLC not an option only due to memory and speed and not non-functioning spi1, right? (I'm just planning a board using both SPIs of the TLC)
 
SPI1 does indeed work on Teensy LC.

But with much less memory and CPU speed, and many of the peripherals lacking powerful features found on the larger chips, I'd highly recommend getting your application well developed before you commit to the hardware.
 
Ok, thanks for the info, I'm using a 3.2, I've ordered a 3.5 to have a play with, although I'm stuck with the 3.2 pinout on this PCB.

The 2 solutions are to edit the display code to use shorter transactions, or to use a separate SPI bus (really only an option on Teensy 3.5 & 3.6).

My guess is that it's the fillRect() function in the library that's doing it, would I be right in thinking that breaking up the transaction loop with endTransaction() and beginTransaction() would allow a queued transaction to take place during a call to fillRect()?
 
I've had a play around with the TFT library using a GPIO pin of an SPI GPIO expander to generate an audio tone instead of the DAC to make life a little easier, retriggering transactions during the fillRect() loops certainly made a difference, by restarting the transaction every line of pixels, the period of the glitches decreases (shorter blocking period) and by forcing a new transaction every pixel it they mostly away. still not really usable with the DAC at audio frequency but an interesting experiment none the less.

Thanks for the help!
 
There is one simple and easy solution: use a Teensy with 2 SPI buses (3.5/3.6) -- no buts, ifs, or maybes, and that approach requires a minimum amount of fairly trivial code change. (but possibly a very significant HW change)

If you can't have 2 buses, then you can either interrupt/pause the display update at some suitable point to service your DAC, or do the display update in small enough chunks that it won't interfere with the DAC update.
When you use shorter display transactions, make sure they start right after the DAC was written, so you'll have as much time as possible.

What is your SPI clock speed? That has a significant impact on attainable throughput.
 
45kHz is 22uS per update, most screens I have played with cant even change 1 letter on screen in that amount of time. You really should look at running them on their own SPI buses.
 
Which library and which display are you using?

ILI9341_t3 library and a generic 2.8" 320x240 TFT (TJCTM24028-SPI)

The clock speed has been left with the default define of "SPICLOCK 30000000" so my understanding is that it will be running at the max speed available.

Thanks guys, I realize a board with a second bus is required to reach that sample rate with a DAC but it was an interesting exercise in moving the transactions non the less. I've got a 3.5 now so I'll have a go with the second bus when I get a chance.
 
As others mentioned having a second bus should help.

As you are using the interval timer to access the DAC, it may not be an issue. But thought I would mention, that even with two or three SPI busses calls to things like tft.fillRect(....) will not return until the full fill rect has completed. Which in your case may not be an issue.

If it does become an issue, I have a version of the ili9341_t3 library (..._t3n), which added support for having a frame buffer. However the frame buffer currently eats a lot of memory... 320*240*2 bytes... so I mainly have tried it on T3.6. But with the Frame buffer, I have support to be able to do the screen updates in the background using DMA.. However it has been awhile and I don't remember if I tried running this part on a T3.5. Should work on SPI, but may have issues on SPI1 and SPI2 as for some reason the T3.5 SPI1/SPI2 each only have one DMA_SOURCE, which can either be RX or TX... Not sure if I put in code to handle it here or not... Did in the SPI library for background SPI transfers: SPI.transfer(txbf, rxbuf, cnt, event_handler)
 
Status
Not open for further replies.
Back
Top