ILI9341 with fullscreen DMA Buffer for Teensy 3.5 / Teensy 3.6 only

Status
Not open for further replies.
Is this something you found in the processor bugs list, or is it something you assumed from your testing ?
I looked at the silicon bugs list, and I could not link this to any.

I assume this from testing - perhaps there's still a way to use it somehow - but i don't know this way.
From the desription and datasheets, the DMA engines schould be the same - but they are not ( otherwise it would work on T3.5 ;-) )
I had no luck with the interrupts, too. They occur with a somewhat random timing.. but on 3.5 only, too. Did I miss something ?

Anyway, there's still the refreshOnce() - it works on both, now.
 
Last edited:
Firstly thanks Frank for all your great work here. Kurt too!

I am using the library with a 3.6, tft and esp.
It is working a treat to play wav from the onboard SD. just uncomment msec=0;
I run into a problem when I call peak1.available(). It looks like there is a DMA conflict of some kind. The audio out of the usb does not sound right.
I am using the original SPI pins not the alternate ones. Is this interfering with the clock somehow?
My guess is the DMA as playback is fine when peak1.available() is not called.

Here is the code

Code:
//#include <ILI9341_t3.h>
#include <ILI9341_t3DMA.h>
#include <font_Arial.h> // from ILI9341_t3
 // include the SD library:
#include <SD.h>
#include <SPI.h>
const int chipSelect = BUILTIN_SDCARD;  
#include <Audio.h>
#include <Wire.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioPlaySdWav           playSdWav1;     //xy=154.66665649414062,158.66666412353516
AudioAnalyzePeak         peak1;          //xy=398.6666564941406,215.66666412353516
AudioAnalyzePeak         peak2;          //xy=404.6666564941406,290.66666412353516
AudioOutputUSB           usb1;           //xy=446.6666564941406,159.66666412353516
AudioConnection          patchCord1(playSdWav1, 0, peak1, 0);
AudioConnection          patchCord2(playSdWav1, 0, usb1, 0);
AudioConnection          patchCord3(playSdWav1, 1, peak2, 0);
AudioConnection          patchCord4(playSdWav1, 1, usb1, 1);
//AudioControlSGTL5000     sgtl5000_1;     //xy=166.66665649414062,309.66666412353516
AudioOutputI2S           out1; // if I don't have this, it won't work at all. Bug or feature?
// GUItool: end automatically generated code

#define TFT_DC      9
#define TFT_CS      10
#define TFT_RST     255  // 255 = unused, connect to 3.3V
#define TFT_MOSI    11
#define TFT_SCLK    13
#define TFT_MISO    12
//ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
ILI9341_t3DMA tft = ILI9341_t3DMA(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);

void setup() {
  //Serial.begin(9600);
  //delay(500);
  tft.begin();
  tft.dfillScreen(ILI9341_BLACK);
  tft.setTextColor(ILI9341_YELLOW);
  //tft.setFont(Arial_24);
  //tft.setTextSize(3);
  tft.setCursor(40, 8);
  tft.println("Peak Meter");
  tft.refreshOnce();
  
  AudioMemory(10);
  //sgtl5000_1.enable();
  //sgtl5000_1.volume(0.5);
  //SPI.setMOSI(7);
  //SPI.setSCK(14);
  if (!(SD.begin(chipSelect))) {
    while (1) {
      //Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
  delay(1000);
}

elapsedMillis msecs;

void loop() {
  if (playSdWav1.isPlaying() == false) {
    //Serial.println("Start playing");
    //playSdWav1.play("SDTEST1.WAV");
    //playSdWav1.play("SDTEST2.WAV");
    playSdWav1.play("SDTEST5.WAV");
    //playSdWav1.play("SDTEST4.WAV");
    delay(10); // wait for library to parse WAV info
  }
  
      //msecs = 0;
  if (msecs > 15) {
    if (peak1.available() && peak2.available()) {
      msecs = 0;
      float leftNumber = peak1.read();
      float rightNumber = peak2.read();
      //Serial.print(leftNumber);
      //Serial.print(", ");
      //Serial.print(rightNumber);
      //Serial.println();

      // draw the verticle bars
      int height = leftNumber * 240;
      tft.dfillRect(60, 280 - height, 40, height, ILI9341_GREEN);
      tft.dfillRect(60, 280 - 240, 40, 240 - height, ILI9341_BLACK);
      height = rightNumber * 240;
      tft.dfillRect(140, 280 - height, 40, height, ILI9341_GREEN);
      tft.dfillRect(140, 280 - 240, 40, 240 - height, ILI9341_BLACK);
      // a smarter approach would redraw only the changed portion...

      // draw numbers underneath each bar
      //tft.setFont(Arial_14);
      tft.dfillRect(60, 284, 40, 16, ILI9341_BLACK);
      tft.setCursor(60, 284);
      tft.print(leftNumber);
      tft.dfillRect(140, 284, 40, 16, ILI9341_BLACK);
      tft.setCursor(140, 284);
      tft.print(rightNumber);

      //AudioNoInterrupts();
      tft.refreshOnce();
      //AudioInterrupts();
    }
  }
}

I know it is very early days for this library. I am surprised it is working so well for me already. I have had not problems using it with all the other libraries I have tried. i2C_t3, Adafruit_MCP23017, OSC, SPI, and even Encoder. Wish it did conflict with Encoder, that one could use some DMA.

Thanks also four your work with SPDIF and PT8211. I have the toslink hooked up and have a ten pack of the Dac's on the way to try.
 

Hi Vic,

Yes, i during the past days/weeks i had not much time to work on it, but my other project is now at a state where i need this ili9341-dma lib myself :)
I hope, that i can find the time to work on it, now.

I'll try your sketch tomorrow. At a first glance: I would comment-out the line AudioOutputI2S out1; The Audio-lib does not like more than one output, as far as i know.. (but never tried it ;-)
 
Hm, i downloaded Teensyduino 1.31 - USB Output does not work at all for me (?) even without any graphics lib.
 
I tried using alternate pins for SPI as if I had the audio board. No joy.
Swapped AudioOutputI2S for AudioOutputSPDIF as you suggested. Joy!
The good news is peak.available is fine as a block comment on the tft stuff inside the peak loop meant the SD card kept playing fine.
They screen is displaying correctly what is reads from peak.available. It matches the bad audio coming out the usb.

I looks like you narrowed down the problem from the posts following the one you linked to yesterday. When I had trouble with the Serial+Audio+Midi on Win7, I bought a second hand Mac mini just to have another platform for testing and dev. I was that keen for it all to fly.
I just checked Win7 and the Serial+Audio+Midi is all working as expected, same as OSX.

The new Teensyduino update did change the usb a fair bit and might need a registry cleanup of the old ports.
You don't need my advice but I am keen to help in any way I can.

I still have to try it without reading from the sd, using playMem1. That may work. I will be sure to let you know if it does.
 
It has something todo with the Spi-transactions..

The Waveplayer uses a macro "AudioUsingSPI" which is part of the problem.
Then, on the 3.6 it is not using SPI anyway..

I just found an other problem: usingInterrupt(uint8_t n) in SPI.h is missing the new T3.5/3.6 Pins. I'll create a pullrequest for Paul for this problem, first. Step by step...
 
Nice work. You are onto it.

I just ran a sketch with playMem1 and it also did not work. It almost worked for the left channel(noisy but in time) but the right was mute.

usingInterrupt may have an issue with the correct PIN number. Paul had a solution for the i2c_t3 interrupts for the MCP23017.
He suggested digitalPinToInterrupt to target the correct PIN numbers for us on the teensy and not the Arduino port numbers.
It will not be the issue inside SPI.h but may help people getting examples working.
like so
Code:
// Define MCU Interrupt Pin and Interrupt Vector
byte mcuIntPin=6;
byte mcuInterrupt=digitalPinToInterrupt(mcuIntPin);

These changes are already moving very fast since the last KickStarter. Some of them are very big steps.
I am blown away by how fast Paul got so much in this last release.

How is the flexiboard 2 going? Can it be soldered by hand. I am pretty keen on one. I have no surface mount experience yet but now is the time to start.
I am in Australia so I could probably use parts as postage out here is a killer for small orders.
 
Nice work. You are onto it.

I just ran a sketch with playMem1 and it also did not work. It almost worked for the left channel(noisy but in time) but the right was mute.

usingInterrupt may have an issue with the correct PIN number. Paul had a solution for the i2c_t3 interrupts for the MCP23017.
He suggested digitalPinToInterrupt to target the correct PIN numbers for us on the teensy and not the Arduino port numbers.
It will not be the issue inside SPI.h but may help people getting examples working.
like so
Code:
// Define MCU Interrupt Pin and Interrupt Vector
byte mcuIntPin=6;
byte mcuInterrupt=digitalPinToInterrupt(mcuIntPin);

These changes are already moving very fast since the last KickStarter. Some of them are very big steps.
I am blown away by how fast Paul got so much in this last release.

How is the flexiboard 2 going? Can it be soldered by hand. I am pretty keen on one. I have no surface mount experience yet but now is the time to start.
I am in Australia so I could probably use parts as postage out here is a killer for small orders.

https://github.com/PaulStoffregen/SPI/pull/21/files

flexiboard2: I've ordered it from Seeed last weekend, and got an email that it shipped. I wonder how long it takes? Mabye 3..5weeks. I used bigger parts this time, 0806 is "OK", not too small. I've not much experience, too (the first time i soldered SMD "dust" was for the Memoryboard :) I need glasses, but with a good magnifying glass it quite ok.
I've ordered much (or "many"? ohh..my English..) parts, if you need some: i can send you the board and some parts (but not ESP8266+Display+RAM: i don't have spare.. (but i have FLASH (w25q128), if you need a chip..)
 
Last edited:
At the moment, it works best to add

(workaround)
Code:
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
#undef AudioStartUsingSPI()
#define AudioStartUsingSPI()
#undef AudioStopUsingSPI()
#define AudioStopUsingSPI()
#endif

to the code of play_sd_wav.cpp. I'm not sure that i understand the code of SPI.usingInterrupt correctly (why exactly does SPI.beginTransaction disable interrupts?) , and what it does with SD.h on T3.5/36, since it handles the card with SDHC, not SPI...

PAUL??? Can you help ?
Even without USB (which does not work for me, with TD131), and PT8211 instead, there are heavy distortions with viclabs code above. It's because the SPI needs too much time to update the display. But The 3.5/3.6 does not need SPI to read the card. So, there is no reason to block anything. I'm not sure, what the best fix is - the workaround in this post works.
An other way to make iclabs' sketch work (with PT8211), is to use refresh() instead refreshOnce() from my lib - it blocks the SPI-Bus permanently (so why does the waveplayer work at all??) - but that is not the correct solution.
 
Last edited:
i posted a issue on github earlier, with request for a feature, namely HUD overlays seperate from the actual framebuffer.

github url: https://github.com/FrankBoesing/ILI9341_t3DMA/issues/9

i started working on this, and i can allocate to a dynamic buffer.

only issue i'm having is: how do i get it to send with DMA? (again, i don't have a teensy 3.6 to test, so i'll paste my test code below for you, needs some porting and library integration though)

again, it is just a basic sample! i don't even know if i'm doing it right ^^;

code:
Code:
//test code for arduino mega:

uint8_t layers[3];

void setup(void) {
  Serial.begin(9600);
  Serial.print(F("Free RAM on INIT "));
  Serial.println(freeram());
  layers[0] = layer_add(0,10,10);
  if (!layers[0]) {
    Serial.println(F("Failed to initialize layer 1!"));
  } else {
    Serial.print(F("Free RAM (added one layer of 10x10px) "));
    Serial.println(freeram());
    Serial.print(F("Layer 0 ID: "));
    Serial.println(layers[0]);
  }
  layers[1] = layer_add(1,20,100);
  if (!layers[1]) {
    Serial.println(F("Failed to initialize layer 2!"));
  } else {
    Serial.print(F("Free RAM (added one layer of 20x100px) "));
    Serial.println(freeram());
    Serial.print(F("Layer 1 ID: "));
    Serial.println(layers[1]);
  }
  layers[2] = layer_add(2,150,100);
  if (!layers[2 ]) {
    Serial.println(F("Failed to initialize layer 3!")); //will result in true, as we're allocatin too much RAM.
  } else {
    Serial.print(F("Free RAM (added one layer of 150x100px) "));
    Serial.println(freeram());
    Serial.print(F("Layer 2 ID: "));
    Serial.println(layers[2]);
  }
}
void loop(void) {}


//ram screenbuffer code overlays here
const uint8_t max_layers = 4;
uint8_t number_overlays = 0; //total amount of overlays set

struct Overlay {
  uint16_t *buffer; //pointer to the buffer 
  int16_t startx, starty, width, height; //coordinate vars
  bool active; //is the overlay active on the screen?
  uint8_t priority;
} overlay[max_layers];

int freeram() {
  extern int __heap_start, *__brkval;
  int v;
  return (int)&v- (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

uint8_t layer_add(uint8_t priority, uint16_t width, uint16_t height) {
  //HARD LIMIT OVERLAY AMOUNT
  if (number_overlays < max_layers) {
    //MIN RAM NEEDED, PLUS 1024 BYTES SO YOU CAN CALL OTHER CONCTIONS AS WELL
    int ram_needed = (width*height)*2+sizeof(Overlay) + 1024;
    if (freeram() >= ram_needed) {
      uint16_t *buffer_;
      buffer_ = (uint16_t *) malloc((width*height)*sizeof(uint16_t));
      overlay[number_overlays].buffer = buffer_;
      overlay[number_overlays].startx = 0;
      overlay[number_overlays].starty = 0;
      overlay[number_overlays].priority = priority; //todo: check if priority is taken
      overlay[number_overlays].width = width; //todo: check if width < screen_width
      overlay[number_overlays].height = height; //todo: check if height < screen_height
      overlay[number_overlays].active = 0; //todo: assign it to the dma channel?
      number_overlays++;
      return number_overlays; //SUCCESS!
    } else {
      return 0; //FAIL
    }
  } else {
    return  0; //FAIL
  }
}
 
hope you feel better soon, frank!

also, seemingly the teensy 3.6 can do a full 240*320px screenblend in < 37ms (you might need to optimize the code for faster speeds) with F_CPU at 240mhz!
i created a pull request on github for the demo code.

edit: got it down to <18ms per blend (240mhz), will put a pull request plus better demo up soon.
 
Last edited:
@pix-os, how fast is it without overclocking to 240MHz ? I mean, theorically, if you can refresh the screen faster than 40ms, it's enough for displaying 25frames per sec.
If you reach 18ms per frame @240MHz, a rule of 3 gives 24ms per frame @180MHz. I understand rule of 3 may not be applicable, but I reckon it won't be as slow as 40ms.

Is there really a point overclocking to 240MHz if optimized code can already display more than 25fps at 180MHz ?
 
your calculation is right, a bit less than 24ms per blend.

and yes, the point of overclocking is: to get more performance than default.

meaning that if you do some fullscreen blends, you can easily get to 100ms delay time with only one fullscreen blends at 180mhz, leaving alone what other code does, like video playback, sd reading, audio triggering, touch reads, parsing touch commands to functions, ect

so boosting the performance simply gives more headroom to process everything ;)

i hope this answer is clear anough.
 
trying to port the video player example for the new lib, it 'works', but i don't have a tft to actually complete my tests if they really work.

right now alot is happening code wise, i'm at 240mhz cpu clock and use about 87% cpu while playing the video file (can't get audio to work :S )

i calculated the uloaded cycle counts for the teensy 3.6 @ 240mhz, and it's a tiny bit under 30.000.000 (yes, 30 million), so i simply set the count at 30mil

so i adapted my good old cpu monitor sketch and started monitoring.
sketch:
https://github.com/ramonschepers/Teensy_CPU_usage

i currently have a read buffer of 1280 uin16_t 's, and when i increase that to 4x the amount, i get 86% cpu usage :p

demo code will be in a pull request soon (no date specified as i want to test it first, already ordered a correct lcd)


also, a nice GUI demo will be up as well once i get to test, debug and polish the code. (hopefully something like an old smartphone, like iphone os 1 or so :') )
 
Big thanks to Paul for putting in the fixes so quick!

The latest teensyduino 1.32-beta1 has fixed the blocking.

Here is the working code

Code:
#include <ILI9341_t3DMA.h>
#include <SD.h>
#include <SPI.h>
const int chipSelect = BUILTIN_SDCARD;
#include <Audio.h>
#include <Wire.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioPlaySdWav           playSdWav1;     //xy=154.66665649414062,158.66666412353516
AudioAnalyzePeak         peak1;          //xy=398.6666564941406,215.66666412353516
AudioAnalyzePeak         peak2;          //xy=404.6666564941406,290.66666412353516
AudioOutputUSB           usb1;           //xy=446.6666564941406,159.66666412353516
//AudioOutputSPDIF         spdif1;         //xy=447.13043212890625,89.13043212890625
AudioOutputPT8211         spdif1;         //xy=447.13043212890625,89.13043212890625
AudioConnection          patchCord1(playSdWav1, 0, peak1, 0);
AudioConnection          patchCord2(playSdWav1, 0, usb1, 0);
AudioConnection          patchCord3(playSdWav1, 0, spdif1, 0);
AudioConnection          patchCord4(playSdWav1, 0, spdif1, 1);
AudioConnection          patchCord5(playSdWav1, 1, peak2, 0);
AudioConnection          patchCord6(playSdWav1, 1, usb1, 1);
// GUItool: end automatically generated code

#define TFT_DC      20   //9-20  Standard-Audio
#define TFT_CS      21  //10-21
#define TFT_RST     255 //255 = unused, connect to 3.3V
#define TFT_MOSI    7  //11-7
#define TFT_SCLK    14  //13/14
#define TFT_MISO    12  //12=12
ILI9341_t3DMA tft = ILI9341_t3DMA(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);

void setup() {
  tft.begin();
  tft.dfillScreen(ILI9341_BLACK);
  tft.setTextColor(ILI9341_YELLOW);
  tft.setCursor(40, 8);
  tft.println("Peak Meter");
  tft.refreshOnce();

  AudioMemory(4);

  if (!(SD.begin(chipSelect))) {
    while (1) {
      //Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
}

elapsedMillis msecs;

void loop() {
  if (playSdWav1.isPlaying() == false) {
    playSdWav1.play("SDTEST5.WAV");
    delay(10); // wait for library to parse WAV info
  }

  if (peak1.available() && peak2.available()) {
    msecs = 0;

    float leftNumber = peak1.read();
    float rightNumber = peak2.read();

    // draw the verticle bars
    int height = leftNumber * 240;
    tft.dfillRect(60, 280 - height, 40, height, ILI9341_GREEN);
    tft.dfillRect(60, 280 - 240, 40, 240 - height, ILI9341_BLACK);
    height = rightNumber * 240;
    tft.dfillRect(140, 280 - height, 40, height, ILI9341_GREEN);
    tft.dfillRect(140, 280 - 240, 40, 240 - height, ILI9341_BLACK);

    // draw numbers underneath each bar
    //tft.setFont(Arial_14);
    tft.dfillRect(60, 284, 40, 16, ILI9341_BLACK);
    tft.setCursor(60, 284);
    tft.print(leftNumber * 100);
    tft.dfillRect(140, 284, 40, 16, ILI9341_BLACK);
    tft.setCursor(140, 284);
    tft.print(rightNumber * 100);

    tft.refreshOnce();
  }
}

Notice I was able to remove the elapsed mills wait because this can run so fast(realtime).

The new meter looks very nice and has minimal flicker. Accurate and quick.

Good chance this is ready to integrate into the already optimised library.

You will notice from the code I am using the PT8211. I got some 3.5 solder and was very pleased to pin down my first SMD. It was a great one to try for the first go. Not a stress with a cheap part and breakout. It will be the first of many. All good for when Flexiboard II arrives.

Thanks again Frank. This will be super useful. Its impressive.
 
@Frank B, just searching the thread it looks like using the same SPI lines to talk to anything else is out of the question. Is that still correct?
 
Still unchanged - that's the feature :) constant screen-updates, unless you pause it.
I have an extended+updated version, not published, yet.

If i ever publish it, it will have a very restrictive "not commercial" license.
(Bad experiences, recently)
 
Status
Not open for further replies.
Back
Top