Teensy4.0, ILI9341_t3n double buffer zone, crashes the board

jean

Well-known member
In my program, I created a double buffer for a given area.
This buffer displays colored lines based on the position of an MPU6050 sensor, but every time, sooner or later, it crashes the board.
I've been trying to figure out why for two weeks without finding an answer.
I don't have any pixels outside the buffer.
I've tried lots of different things, but the Teensy 4.0 still crashes.
I'll spare you the semantics I've done on my code.
teensy 4.0
ILI9341_t3n
cpu speed 816
Wire.setClock(528000L); even when setting it to 400000, the effect is the same.

Memory Usage on Teensy 4.0:
FLASH: code:80048, data:26208, headers:8428 free for files:1916932
RAM1: variables:53792, code:76152, padding:22152 free for local variables:372192
RAM2: variables:12416 free for malloc/new:511872


Buffer definitions:
C++:
// === Définition buffer A ===
#define BUF_W 111     // largeur de la zone
#define BUF_H 138     // hauteur de la zone
#define OFFSET_X 105  // position X sur l'écran
#define OFFSET_Y 32   // position Y sur l'écran
uint16_t buffer1[BUF_W * BUF_H];
uint16_t buffer2[BUF_W * BUF_H];
or:
//DMAMEM uint16_t buffer1[BUF_W * BUF_H];
//DMAMEM uint16_t buffer2[BUF_W * BUF_H];
Same result.

Functions for loading it:

C++:
void drawPixelToBuffer(int x, int y, uint16_t color) {
  if (!bufferReady) return;
  bufferReady = false;
  int bx = x - OFFSET_X;
  int by = y - OFFSET_Y;
  /* // Filtre pour éviter tout hors-buffer
    if (bx < 0 || bx >= BUF_W || by < 0 || by >= BUF_H) {
        // Optionnel : log pour debug
        Serial.print(F("Pixel hors zone: ("));
        Serial.print(x);
        Serial.print(F(","));
        Serial.print(y);
        Serial.println(F(")"));
        return; // on sort sans écrire
    }
    */
  activeBuffer[by * BUF_W + bx] = color;
  bufferReady = true;  // buffer prêt après écriture
}

void drawLineToBuffer(int x0, int y0, int x1, int y1, uint16_t color) {
  //Serial.printf("drawLineToBuffer: (%d,%d) -> (%d,%d)\n", x0, y0, x1, y1);
  int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
  int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
  int err = dx + dy, e2;
  while (true) {
    drawPixelToBuffer(x0, y0, color);
    if (x0 == x1 && y0 == y1) break;
    e2 = 2 * err;
    if (e2 >= dy) {
      err += dy;
      x0 += sx;
    }
    if (e2 <= dx) {
      err += dx;
      y0 += sy;
    }
  }
}

How to use it:

The function that uses the buffer and is called at each loop iteration.

void My function() {
Calculations based on the sensor
then

Two loops with a maximum of 210 iterations

C++:
drawLineToBuffer(x1,y1,x2,y2, RED);

then

C++:
tft.writeRect(OFFSET_X, OFFSET_Y, BUF_W, BUF_H, activeBuffer);
  memset(activeBuffer, 0, sizeof(uint16_t) * BUF_W * BUF_H);

uint16_t* temp = activeBuffer;
  activeBuffer = displayBuffer;
  displayBuffer = temp;

  swapBuffers();
  bufferReady = true;

End of function.

I don't know what else to do to make everything work properly. Thank you to anyone who takes the time to respond and help me with my problem.

I also have a second, smaller double buffer. This one does not cause any problems.
I also have a normal display, so the resources also use the sensor data, no problem.
 
Last edited:
I haven't used ILI9341_t3n much, but my understanding is it needs the buffers of the screen size. Is that a wrong assumption on my part?

Looking at ILI9341_t3n doubleBuffer.ino example, the buffers are 320 x 240 pixels.

Code:
#define TFT_DC  9
#define TFT_CS  10
#define TFT_RST 8
#define CENTER ILI9341_t3n::CENTER
DMAMEM uint16_t fb1[320 * 240];
DMAMEM uint16_t fb2[320 * 240];

Your program is creating only tiny buffers of 111 x 138 pixels.

Code:
// === Définition buffer A ===
#define BUF_W 111     // largeur de la zone
#define BUF_H 138     // hauteur de la zone
#define OFFSET_X 105  // position X sur l'écran
#define OFFSET_Y 32   // position Y sur l'écran
uint16_t buffer1[BUF_W * BUF_H];
uint16_t buffer2[BUF_W * BUF_H];
 
Hello Paul, yes, I saw the example. Can't we use a double buffer for a small area?
The second one, which is smaller, works very well.
 
As far as I can tell, this has nothing to do with the frame buffer code within the library.
That is I don't see anything here that shows any of the APIs associated with it.

You simply have your own code with it's own buffers, that you then use the writeRect function to output to the screen.
Which is fine.

A couple of things I would check include:
a) double check that you are never going outside the bounds of your arrays, looks like you have code commented out that
does that, so guessing you have been trying that.

b) Check your memory usage. You did not post any of the build information, maybe your buffers and stack are colliding.

c) Maybe processor overheating or the like, try running at lower speed, like the default 600mhz.

Good luck
 
Hello, @KurtE,

Yes, I check if I have any points outside the buffer. I don't have any.

I use very few resources in this function, and there is normally enough space available.

I made the change, but I'm still getting random crashes (sometimes the screen freezes, but the Arduino serial monitor continues to display the logs).

Stack utilisée : 112 octets
Avant affichage | activeBuffer: 0x20200000 | displayBuffer: 0x20206A00 | diff (octets): 27136
Après swap | activeBuffer: 0x20206A00 | displayBuffer: 0x20200000 | diff (octets): -27136

Regardless of the card speed, I get the same result.

I tested with only this function active, and got the same result.
Memory Usage on Teensy 4.0:
FLASH: code:88240, data:23452, headers:9136 free for files:1910788
RAM1: variables:44544, code:84344, padding:13960 free for local variables:381440
RAM2: variables:66688 free for malloc/new:457600
 
Hello everyone,
I don't know if this is the right solution technically speaking, but I added an if condition that encompasses my for loops for display. It works, but it may not be clean. In any case, it no longer crashes the map.
 
No idea what that meant, but what if condition... i.e. would be good to know if there is a bug in the underlying library or something else.

Kurt
 
but I'm still getting random crashes (sometimes the screen freezes, but the Arduino serial monitor continues to display the logs).

Maybe the SPI clock is too fast?

Does ILI9341_t3n have an API for configuring clock speed? Or does it required editing inside the library? Either way is fairly easy, once you know the right place to edit.
 
Maybe the SPI clock is too fast?

Does ILI9341_t3n have an API for configuring clock speed? Or does it required editing inside the library? Either way is fairly easy, once you know the right place to edit.
Code:
void begin(uint32_t spi_clock = ILI9341_SPICLOCK,
             uint32_t spi_clock_read = ILI9341_SPICLOCK_READ);
 
My copy of ILI9341_t3n looks like its default is 30 MHz.

@jean - How long is the wiring between Teensy and the display? Is the GND wire physically close to the clock? This is just blind guessing (trying to help over the internet) but perhaps try a slower SPI clock? Or try adding a 100 ohm resistor between Teensy and the SPI clock wire, because the resistor can help tame overshoot and other nasty high speed signal quality problems if the wire is "long".
 
Last edited:
@KurtE
I did not change the SPI speed.
SPI Clock (écriture) par défaut = 30000000
Horloge SPI (cours) par défaut = 2000000

@Paul
The screen is soldered onto an intermediate PCB and the Teensy 4.0 is on the other side of the same intermediate board.
At first, I thought it was a synchronization problem.
 
Jean, have you tried very basic sketches or examples, in order to isolate the problem: is it the hardware, the 816MHz cpu speed, SPI speed, your software, the buffer declaration or the ILI9341-t3n library.
 
Last edited:
@ange
Yes, of course, in the ILI9341-t3n library there is no zone buffer, so I tried to do it myself. In my program, I have two doubles, the other one has been working fine since the beginning. Adding a flag to display the buffer if I have a status change works much better than crashing. So I think it's either a synchronization or SPI speed issue. As Paul says, I'm going to test with lower speeds to see what the result is.
 
I would try the examples included with the library repo. No modifications. They should run fine. Then changing CPU and/or SPI speeds will validate your hardware, power supply,....

Is the ILI9341 fast enough, to support large DMA, fast refresh,...? Maybe you should check for transfer completion before starting a new one. In the ILI9341 controller, and in the DMA controller. Unless it is done int the library.
 
@ange
Yes, of course, in the ILI9341-t3n library there is no zone buffer, so I tried to do it myself. In my program, I have two doubles, the other one has been working fine since the beginning. Adding a flag to display the buffer if I have a status change works much better than crashing. So I think it's either a synchronization or SPI speed issue. As Paul says, I'm going to test with lower speeds to see what the result is.
You are right there are no secondary buffers, however if you are using the frame buffer mechanism within the library,
you can set a clip rectangle, do all of your updates and then do updateScreen() and if the clip rectangle is still in place
it will only update that rectangle on the display... updateScreenAsync earlier did not have the support for only doing the partial screen,
but I know I have a branch that does do that, and I don't remember if I finally merged it in to main branch
 
Bonjour à tous,
J'ai effectué plusieurs tests, tous insatisfaisants. Le seul qui empêche la carte de planter est ma condition dans la fonction qui l'appelle : if(position!=oldposition){charger le tampon}else{ne rien faire}. Cela fonctionne sans problème depuis deux jours.
Donc, pour ma part, je vais m'en tenir à cette solution.
 
I've been vaguely following this thread without having anything to contribute, then this morning a couple of points occurred to me.
Wire.setClock(528000L); even when setting it to 400000, the effect is the same.
Not relevant - Wire is for I²C, not SPI.

Memory Usage on Teensy 4.0:
FLASH: code:80048, data:26208, headers:8428 free for files:1916932
RAM1: variables:53792, code:76152, padding:22152 free for local variables:372192
RAM2: variables:12416 free for malloc/new:511872

Buffer definitions:
C++:
// === Définition buffer A ===
#define BUF_W 111     // largeur de la zone
#define BUF_H 138     // hauteur de la zone
#define OFFSET_X 105  // position X sur l'écran
#define OFFSET_Y 32   // position Y sur l'écran
uint16_t buffer1[BUF_W * BUF_H];
uint16_t buffer2[BUF_W * BUF_H];
or:
//DMAMEM uint16_t buffer1[BUF_W * BUF_H];
//DMAMEM uint16_t buffer2[BUF_W * BUF_H];
Same result.
This is potentially concerning. There are two buffers for 111x138 pixels, which should take 2*111*138*2 = 61272 bytes just by themselves. Yet the memory report says only 53792 bytes for variables, and that's without taking the following into account.
I also have a second, smaller double buffer. This one does not cause any problems.
I also have a normal display, so the resources also use the sensor data, no problem.

I guess what we really need, as usual, is what the Forum Rule requests: post a small complete sketch which can be compiled and run using the Arduino IDE and demonstrates the issue. Without that, we're just making wild guesses...
 
@h4yn0nnym0u5e
Hello,
I tested extensively with the SPI bus, and I also have an I2C bus. I wanted to make sure they didn't interfere with each other.

The latest compilation gives:
Memory Usage on Teensy 4.0:
FLASH: code:97712, data:151052, headers:8256 free for files:1774596
RAM1: variables:59488, code:93816, padding:4488 free for local variables:366496
RAM2: variables:66688 free for malloc/new:457600

My three double buffers:

C++:
// === Définition buffer A ===
#define OFFSET_X 107  // position X sur l'écran
#define OFFSET_Y 39   // position Y sur l'écran
#define BUF_W 106     // largeur de la zone
#define BUF_H 128     // hauteur de la zone
DMAMEM __attribute__((aligned(32))) uint16_t buffer1[BUF_W * BUF_H];
DMAMEM __attribute__((aligned(32))) uint16_t buffer2[BUF_W * BUF_H];

// === Définition buffer B ===
#define BUFALT_W 50      // largeur de la zone
#define BUFALT_H 122     // hauteur de la zone
#define OFFSETALT_X 265  // position X sur l'écran
#define OFFSETALT_Y 47   // position Y sur l'écran
uint16_t bufferalt1[BUFALT_W * BUFALT_H];
uint16_t bufferalt2[BUFALT_W * BUFALT_H];

// === Définition buffer C ===
#define BUFKTS_W 30      // largeur de la zone
#define BUFKTS_H 122     // hauteur de la zone
#define OFFSETKTS_X 24  // position X sur l'écran
#define OFFSETKTS_Y 47   // position Y sur l'écran
uint16_t bufferkts1[BUFKTS_W * BUFKTS_H];
uint16_t bufferkts2[BUFKTS_W * BUFKTS_H];

The one that was causing me problems was buffer A; the other two have been working fine since the beginning.

I gave my solution, which is a bit unusual, just a condition of whether or not to load the buffer. I know it's not normal, but for now, it's the only one that works.
 
Bonjour à tous,
J'ai effectué plusieurs tests, tous insatisfaisants. Le seul qui empêche la carte de planter est ma condition dans la fonction qui l'appelle : if(position!=oldposition){charger le tampon}else{ne rien faire}. Cela fonctionne sans problème depuis deux jours.
Donc, pour ma part, je vais m'en tenir à cette solution.
Translated>
Hello everyone,

I performed several tests, all unsatisfactory. The only thing that keeps the map from crashing is my condition in the function that calls it: if(position!=oldposition){load buffer}else{do nothing}. It has been working without problem for two days.

So, for my part, I'm going to stick with this solution.
 
I gave my solution, which is a bit unusual, just a condition of whether or not to load the buffer. I know it's not normal, but for now, it's the only one that works.
Well sort of, yes, but it's just a line of pseudo-code with no context. You could just be bodging around a bug which will re-surface at the most embarrassing moment possible.

If you really truly want help with a proper solution, you need to post a complete compilable and runnable sketch. There Is No Other Way.

Apart from anything else, the effort you put in to strip away all the irrelevant code may show you what you've done wrong. (Most people respond to that by either never telling us, or just saying "I found the bug" without revealing what it was. This is not great, because others may want to know how this sort of thing can be avoided. Everyone puts bugs in their code, and a lot of the time when they're found, they go "I can't believe I didn't spot that". I've been coding for 50 years, and still do that. There's no shame.)

If your demo sketch still fails, you may have found a library bug. This happens, too. And you're doing everyone a favour by discovering it - it will probably get fixed, and maybe in a few years' time it might even make it into an official release.

If you post a sketch, I'll give it a go. If not, you won't be hearing from me again.
 
Agreed, we really need programs to reproduce this problem, ideally using only well known modules from Sparkfun or Adafruit so anyone can wire up the same hardware and run the program to reproduce the problem.

Without a way to reproduce the problem, we're just blind guessing. Sometimes on this forum we do solve problems with blind guesses, but obviously that's not working on this case. We really do need a program and clear instructions anyone can use to reproduce the problem.
 
Back
Top