ST7796 Teensyduino support

Running on @KenHahn MINI adjusted to rotate(3) and pins:
#define TFT_DC 9 // 10
#define TFT_CS 10 //27
With edits printing that it is doing 100 loop updates of the 100x100 blocks per second.

That 100/sec is at default 16 MHz SPI.
At 80 MHz it jumps to 448/sec with edit in ...\libraries\ST7735_t3-dev-big-screen-t4\src\ST7735_t3.h
#define ST7735_SPICLOCK 80'000'000
//#define ST7735_SPICLOCK 40'000'000
//#define ST7735_SPICLOCK 24'000'000
//#define ST7735_SPICLOCK 16'000'000

4.5*16 is 72. Setting it to 72MHz gives only 369/sec - so 80 is workable round number. 3.69*16 would be 60 MHz.

Code:
elapsedMillis loopTimer;
void loop()
{
  static uint32_t lpCnt = 0;
  lpCnt++;
  if (loopTimer >= 1000)
  {
    Serial.println(lpCnt);
    lpCnt = 0;
    loopTimer = 0;
  }
  {
    if (!tft.asyncUpdateActive())
    {
      int w = random(TFT_width);
      int h = random(TFT_height);

      if (w >= 380)
        w = 380;
      if (h >= 220)
        h = 220;

      tft.clearChangedArea();
      tft.fillRect(w, h, 100, 100, random(65535));
      tft.changeAsyncClipArea();
      tft.updateScreenAsync(false, true);
    }
  }
}
 
Last edited:
Note that I added (but failed to document in the ReadMe…) a setSPISpeed() method, so you can call tft.setSPISpeed(80’000’000) from your sketch, rather than messing about editing the library source.
 
document in the ReadMe
Good to know. Just as well as I just read the posts here :)
After .begin() I placed tft.setSPISpeed(80000000); and it works with #define still at 16M!

Looks Good!: Did a quick mod to do: tft.fillRect(w, h, 10+random(90), 10+random(90), random(65535));
That is making ~1,475+/-50 rectangle loops per second given the smaller size

The i2s audio not working with the MINI's PJRC Audio so swapped in "AudioControlSGTL5000 audioShield;" code from example 'ToneSweep' and that is working.

Note: p##224 had this unexplained falls (w >= 380) replaced with if().
Code:
// https://forum.pjrc.com/index.php?threads/st7796-teensyduino-support.76510/page-9#post-367434
/*
  Test mit Display ST7796S FrameBuffer mit DMA und Audio
*/

#include <Arduino.h>
#include <Audio.h>
#include <ST7796_t3.h> // Hardwarespezifische Bibliothek
#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 9 // 10
#define TFT_CS 10 //27
#define TFT_RST 9
#define TFT_BL 28 // TFT-Hintergrundbeleuchtung
#define TFT_height 320
#define TFT_width 480

// Für 3,5"-TFT-Bildschirme mit ST7796S
ST7796_t3 tft = ST7796_t3(TFT_CS, TFT_DC, TFT_RST);

/* // Audio initialisieren
  AudioSynthWaveform wav1;
  AudioSynthWaveform wav2;
  AudioOutputI2S audioOutput;
  AudioConnection patchCord1(wav1, 0, audioOutput, 0);
  AudioConnection patchCord2(wav2, 0, audioOutput, 1);
*/

AudioSynthToneSweep myEffect;
AudioOutputI2S      audioOutput;        // audio shield: headphones & line-out

// The tone sweep goes to left and right channels
AudioConnection c1(myEffect, 0, audioOutput, 0);
AudioConnection c2(myEffect, 0, audioOutput, 1);

AudioControlSGTL5000 audioShield;
float t_ampx = 0.8;
int t_lox = 10;
int t_hix = 8000;
// Length of time for the sweep in seconds
float t_timex = 10;


// Der TFT-Framebuffer befindet sich im Teensy4.1 512MB RAM2
DMAMEM uint16_t FrameBuffer[TFT_height * TFT_width];

//=========================================================
void setup()
{
  AudioMemory(20);

  /*  // Wellenform-Einstellungen
    wav1.amplitude(0.5); // Lautstärke 0,0 - 1,0
    wav2.amplitude(0.5); // Lautstärke 0,0 - 1,0
    wav1.frequency(440); // Frequenz in Hz (A4)
    wav2.frequency(440); // Frequenz in Hz (A4)
  */
  audioShield.enable();
  audioShield.volume(0.25);

  // TFT-Hintergrundbeleuchtung initialisieren
  pinMode(TFT_BL, OUTPUT); // TFT-Hintergrundbeleuchtung
  digitalWrite(TFT_BL, HIGH); // TFT_BL an

  // Standard-TFT-Display-Konfiguration
  tft.begin();
  tft.setSPISpeed(80000000);
  tft.setAsyncInterruptPriority(224);
  tft.useIntermediateBuffer(tft.width() * 2 * 2);
  tft.useFrameBuffer(true);
  tft.updateChangedAreasOnly(true);
  tft.setRotation(3);
  tft.invertDisplay(true);
  tft.fillScreen(ST7735_WHITE);
  tft.setCursor(200, 150);
  tft.setTextColor(ST7735_BLACK);
  tft.setTextSize(2);
  tft.print("Hallo Welt");
  tft.updateScreenAsync(false, true);

  if (!myEffect.play(t_ampx, t_lox, t_hix, t_timex)) {
    Serial.println("AudioSynthToneSweep - begin failed");
    while (1);
  }
}

//=========================================================

elapsedMillis loopTimer;
void loop()
{
  static uint32_t lpCnt = 0, UpDn = 1;
  lpCnt++;
  if (UpDn && (! myEffect.isPlaying())) {
    myEffect.play(t_ampx, t_hix, t_lox, t_timex);
    UpDn = 0;
  }
  else if ( ! myEffect.isPlaying()) {
    myEffect.play(t_ampx, t_lox, t_hix, t_timex);
    UpDn = 1;
  }

  if (loopTimer >= 1000)
  {
    Serial.println(lpCnt);
    lpCnt = 0;
    loopTimer = 0;
  }
  {
    if (!tft.asyncUpdateActive())
    {
      int w = random(TFT_width);
      int h = random(TFT_height);

      if (w >= 380)
        w = 380;
      if (h >= 220)
        h = 220;

      tft.clearChangedArea();
      tft.fillRect(w, h, 10 + random(90), 10 + random(90), random(65535));
      tft.changeAsyncClipArea();
      tft.updateScreenAsync(false, true);
    }
  }
}
 
On my prototype, the maximum SPISpeed (60000000) only works. It could be due to the wiring. I used your example and got readings between 1198 and 1260 at a 600MHz CPU clock.
 
Above about 30Mbit/s you start to require careful attention to track impedance and routing, for instance don't expect things to work reliably on a breadboard up at 80Mbit/s, even with careful layout and short wiring.
 
between 1198 and 1260 at a 600MHz CPU clock.
Similar here at 60M SPI on the MINI board with PCB traces about 3" to the display header, about 20% slower than 80MHz that shows it working as well on this board.

Though a second MINI with same display on an additional 4" ribbon connector also works as well at 80MHz.

I don't know if the AudioSynthToneSweep on the PJRC SGTL5000 audioShield relates to your use, but Audio is working without display conflict.
30Mbit/s you start to require careful attention to track impedance and routing
Apparently @KenHahn did work that out successfully on his MINI!

So good work it seems @h4yn0nnym0u5e
 
Because the audio engine (8-voice polyphonic synthesizer with filter, LFO, wavetable, and samples) requires a lot of resources on Teensy4.1.
I think I'll move the GUI to a second Teensy (Teensy 4.0). I'm working with PlatformIO and Visual Studio Code and can program both Teensy devices simultaneously via USB.

Jeannie Board Green1.jpg
 
move the GUI to a second Teensy (Teensy 4.0)
How much data will be needed to feed the GUI?
How much CPU overhead is there in all the User inputs? The real incoming data rate from all those encoders/buttons is trivial from the operator, but the scan and monitoring polling or interrupt looks exhausting :)
 
The audio engine requires up to 80% CPU processing time when 8 voices with ladder filters are used simultaneously.
I have concerns about the size of the 300KB framebuffer. The remaining 200KB will not be sufficient for the audio engine. Putting the framebuffer in PSRAM is not a good alternative since I want to use the PSRAM as sample RAM. Furthermore, I need a fast display for audio signals and bars in the GUI.

Old Projekt with 320 x 240 Pixel TouchDisplay and 153KB FramBuffer
 
Last edited:
Using PSRAM as frame buffer isn’t as slow as you might fear. And we do now have the option of 16MB parts, though I don’t know how easy they are to get hold of.

Having said that, it probably does make sense to have another Teensy just to run the UI, you have a lot going on :)
 
Yes, I think he ordered about 100? Not a large order by semiconductor manufacturers’ standards… And he’s in the USA, and Rolf’s in Deutschland.
 
Open for backorder - but Mouser:

Availability​

Stock:
0

When @KenHahn ordered from DigiKey they claimed some stock - but not as many as he ordered. They didn't ship any partial - waited until the factory could deliver all he ordered ... months later. Then Digikey bumped the MIN order # - which IIRC is what he ordered ... not sure if they got any in stock over that order and changed the MIN#?

Sparkfun ordered after Ken and they show stock - not sure that helps outside the US:
 
Thanks. I always get that mixed up.

Another option. The Teensy 4.0 for the GUI queries the encoders, buttons and touch controllers and controls the parameters for the audio engine and FX via two serial buses. Perhaps this is a better way :unsure:


Screenshot 2026-04-26 212118.png
 
Yes, I think he ordered about 100? Not a large order by semiconductor manufacturers’ standards… And he’s in the USA, and Rolf’s in Deutschland.'
For the record, I brought in 1,000, though if I knew how things were going to go, I would have brought in more on that first order.

For international customers, the best bet right now is probably to order from Sparkfun who has stock on-hand. https://www.sparkfun.com/16-mb-psram-ic-is66wvs16m8fbll.html
 
Wow, that’s quite an investment in a reasonably niche component. Though I guess the hindsight suggests that they’re selling OK, and the problem now is reliable supply for restocking. So grateful for folks like you who put the effort into getting fun stuff into the hands of tinkerers and enthusiasts 👏
 
Back
Top