ST7796 Teensyduino support

Sorry, I misspelled it here.. This is an error in PlotformIO

Error
Screenshot 2026-04-02 202108.png
 
It’s a class method, so you need to say which instance of the ST7796_t3 class you‘re referring to. You only have one, so it’s easy: int PixLimit = tft.getChangedArea();. (I have a rig with 5 displays … at which point maybe it’s more obvious you need to say which one!)
 
"Houston, We Have a Problem!" 🫣

I'm using `useIntermediateBuffer()` and `updateScreenAsync(false, true, true)` for `fillRec()` and `fillScreen()`.
The problem is: Sometimes errors occur immediately, and sometimes only after a few minutes.
I deleted everything in the code that wasn't needed so that it wouldn't interfere with other library functions.
Test with #define ST7735_SPICLOCK 16'000'000, 24'000'000 and 80'000'000 Hz

20260403_201936.jpg
20260403_201924.jpg


Code..
C:
/*
Jeannie II
Polyphonic DIY Synthesizer/Sampler
(c) by Rolf Degen and Andre' Laska März 2026
Version 27.03.2026
Used 3.5 inch TFT ST7796S Display
*/

#define TFT_MISO 12
#define TFT_MOSI 11
#define TFT_SCK 13
#define TFT_DC 8
#define TFT_CS 10
#define TFT_RST 9
#define TFT_BL 28 // TFT Backlight


#include <Arduino.h>
#include <ST7796_t3.h>                // Hardware-specific library
#include <SD.h>
#include <SPI.h>
#include <Wire.h>


// For 3.5" TFT with ST7796S
ST7796_t3 tft = ST7796_t3(TFT_CS, TFT_DC, TFT_RST);


// FrameBuffer is located in Teensy4.1 Ram2
#define TFT_height 320
#define TFT_width 480
DMAMEM uint16_t FrameBuffer[TFT_height * TFT_width];

// Setup -------------------------------------------------------
void setup(void)
{
    Serial.begin(9600);

    // init Background LED
    pinMode(TFT_BL, OUTPUT);       // TFT Backligth
    digitalWrite(TFT_BL, HIGH);     // TFT_Backlight on

    tft.setFrameBuffer(FrameBuffer);
    tft.init(320, 480);
    tft.setRotation(1);
    tft.invertDisplay(true);
    tft.useIntermediateBuffer(tft.width() * 2 * sizeof(uint16_t)); // intermediate buffer
    tft.useFrameBuffer(true);
    tft.updateChangedAreasOnly(true);

}

void loop()
{
   if (!tft.asyncUpdateActive())
    {
        tft.clearChangedArea(); // reset area boundaries - now invalid!
        tft.fillRect(50, 80, 300, 210, random(65353));
        tft.updateScreenAsync(false, true, true);
    }

    delay(1000);
}

Thanks for help :)
 
Last edited:
To be accurate (more or less):
Jack Swigert said:
"Okay, Houston ... we've had a problem"
Jack Lousma said:
This is Houston. Say again, please.
Jim Lovell said:
"Ah, Houston, we've had a problem"
I've just got this up and running, and I've not had a problem. So far.
  • I have DC on pin 9 and CS on pin 29 (reset and backlight are changed too, but unlikely to be relevant...)
  • I can't get 80MHz to work, unsurprisingly, but 60MHz seems OK
  • my ST7796 display is 4"
  • ages ago I removed a diode which was fitted in series with CS on the display, after it caused problems: https://forum.arduino.cc/t/st7796-spi-displays-design-error/689315
  • I recently had to put 47R resistors in series with SCK and MOSI to combat ringing on the high-speed edges
Not sure if any of that will help. Please don't try stirring the oxygen tanks...
 
It seems to be a problem with the connecting ore cables. It works when I press the cables. I connected cables that were 15 cm long.
The plug connection is the problem!
 
Last edited:
Yes. That's a good idea. I'll try it with 50-100 ohms.

68 ohms to MOSI and CLK works well with an SPI clock of 60MHz :) Max SPI speed is 75MHz. 80MHz is too high. The display no longer works.
 
Last edited:
I'm using a @KenHahn MINI board that is abouit 3" T_4.1 to header and then a 4" cable on one that won't socket directly to the header and it works at 80MHz. Not sure if Ken has any resistors on the SPI lines?
 
Small resistors of 10 - 470 ohms in series with the signal lines prevent reflections of the signal at the cable end by acting as a termination resistor and matching the impedance.
 
... thought so ... left as an exercise for the reader :)
Thanks for confirming - I just know Ken made it work.

I have a MINI with TALL T_4.1 headers (Ken's boards use the shorter headers) so I could put in a normally pinned Teensy for testing. That makes it stand up and interfere with display mounting. So a 4" ribbon cable was the best available on hand way to give access and utility to swap in alternate T_4.1's.
 
The encoders and graphics are now working very well. I've structured the image layout into sections. For each encoder, only specific graphic areas need to be updated. This reduces transmission time and improves encoder querying.

Video
 
Houston, we have a problem!
The ST7796S frame buffer is blocking Teensy audio. If I disable the frame buffer, the audio works perfectly.
 
There are issues with DMA and interrupt priorities: see post #106 and some following.
  • it's worth trying just putting in a call to the setAsyncInterruptPriority(int level) method to lower the display's interrupt priority below that of the Audio library, i.e. to 224 or 240 (Audio is 208), This call must be before you do any async updates - straight after your init() call should do it
  • if that's not enough, you need to change the DMAChannel utilities in cores to allow for low- and high-priority DMA - see this thread for discussion. Post #108 has a zip file with changes that worked for me
I don't currently have a display+audio setup to hand to make a simple example - sorry.
 
Sometimes it's enough just to set the I2S watermarks to sane levels, rather than the incorrect Teensy3 settings that were reused for Teensy4.
 
Which line do I need to insert 'setAsyncInterruptPriority(int level)' ?

C:
/*
Test with Display ST7796S FrameBuffer with DMA and Audio
*/

#include <Arduino.h>
#include <Audio.h>
#include <ST7796_t3.h> // Hardware-specific library
#include <SPI.h>
#include <st7735_t3_font_OpenSans.h>

// TFT Display ST7796S
#define TFT_MISO 12
#define TFT_MOSI 11
#define TFT_SCK 13
#define TFT_DC 8
#define TFT_CS 10
#define TFT_RST 9
#define TFT_BL 28 // TFT Backlight
#define TFT_height 320
#define TFT_width 480

// For 3.5" TFT with ST7796S
ST7796_t3 tft = ST7796_t3(TFT_CS, TFT_DC, TFT_RST);

// init Audio
AudioSynthWaveformSine sine1; // xy=285,416
AudioOutputI2S i2s1;          // xy=501,413
AudioConnection patchCord1(sine1, 0, i2s1, 0);
AudioConnection patchCord2(sine1, 0, i2s1, 1);

// TFT FrameBuffer is located in Teensy4.1 512MB Ram2
DMAMEM uint16_t FrameBuffer[TFT_height * TFT_width];

// Setup -------------------------------------------------------
void setup(void)
{
    Serial.begin(9600);

    AudioMemory(16);
    // Waveform-Einstellungen
    sine1.amplitude(0.5); // Lautstärke 0.0 - 1.0
    sine1.frequency(440); // Frequenz in Hz (A4)

    // init TFT Backlight
    pinMode(TFT_BL, OUTPUT);    // TFT Backligth
    digitalWrite(TFT_BL, HIGH); // TFT_BL off

    // init TFT ST7796S
    tft.setFrameBuffer(FrameBuffer);

    tft.init(320, 480);
    tft.setRotation(1);
    tft.invertDisplay(true);
    tft.useIntermediateBuffer(tft.width() * 10 * sizeof(uint16_t)); // intermediate buffer
    tft.useFrameBuffer(true);

    tft.updateChangedAreasOnly(true);
    tft.clearChangedArea();
    tft.fillScreen(ST7735_BLACK);
    tft.updateScreenAsync(false, true, true);
}

//--------------------------------------------------------------
// Main loop
//--------------------------------------------------------------
void loop()
{
    tft.clearChangedArea();
    tft.fillRect(20, 20, 50, 50, random(65353));
    tft.updateScreenAsync(false, true, true);
    delay(500);
}
 
Last edited:
The cause seems to be the `tft.useIntermediateBuffer(tft.width() * 10 * sizeof(uint16_t));` line. If I disable it, audio playback works with FrameBuffer.
 
Back
Top