fastest OLED display ?

Status
Not open for further replies.

iniitu

Member
hello,
i am working on a MIDI project with a Teensy 3.5 that requires each iteration of the main loop to be as fast as possible ( right now around 90 microsec for 1 loop ), because it's managing MIDI delays ( each entering note, sometimes 6 notes/bar at 300BPM, generates 4 delays ).
i realized after trying to optimize different things that the main factor in slowing down the whole process is the displaying on the OLED screen ( through I2C because i had too few 'easily accessible' pins available for SPI )
what would be the best option to minimize as much as possible the time necessary for a display ? i think i read once about OLED that had their own microcontroller to diminish the duty on the main controller.. is that an option ?
Sylvain
 
Hard to say.... Maybe depends on your requirements for the display and the like:

That is if you are not doing anything fancy with the displays, you might for example look at some display that can be controlled by a serial port (UART). I have not used one in a long time, but I think there are some out there like: https://www.digole.com/index.php?productID=540

Then you simply need to have code that outputs the display update information into the Serial port and allow the system to take care of the actual outputs...

IF you were using SPI with something like ST7735, ST7789, ... Where for example we have versions of the software that allow you to update the display using DMA... Then those might work for you.

I have never tried doing something similar for I2C...
 
thanks for the answer, Kurt,
it gives me some good directions to explore.
i dont need to do anything fancy, just display some information once in a while, for instance scrolling a menu
Sylvain
 
Simplest way to speed up the program is only updating the display a certain number of times per second instead of once per loop using a timer. So instead of updating your display 10,000 times a second you can do something reasonable, like 30 or 60 times depending on what you prefer.
 
Depending on your speed/power/etc. requirements, you could consider using something like a Teensy LC to control the display, and have the main teensy communicate to the LC what to write, and let the LC deal with menus, redisplay, etc. Of course you need to be able for the LC to say when it is getting backed up.

As KurtE said, the Dig-ole displays can be configured to take i2c, spi, or UART (I only used UART when I used them). The display is smart, so you only send a few characters to do an update.

I have to imagine anything wtih i2c is going to be slow. Dig-ole might be acceptable, because you won't try to update the whole screen.

Note, in the past when I've switched between LCD and OLED displays, the OLED display ran much slower than the LCD display (this is for uncanny eyes where the processor is trying to update the display as fast as possible). While adding a pull-up resistor helped with corruption issues I was having, I still had to set the SPI bus slower for the Adafruit OLED displays than the Adafruit TFT LCD displays.

I think with the latest TFT LCD displays (the 240x240 and 240x320) from Adafruit, they claim that the display now has a pixel addressable frame buffer, one technique would be to not erase the whole display each time, but just update the bits that changed from the last time.
 
Not sure what you have tried in software or what hardware you have, but I have had good success speeding up a 2.4" Monochrome Oled 128x64 on SSD1309:
1) I2C clock speed from 400khz to 2000khz
2) Blanking lines of text on screen either by re-drawing the old text in black before printing the new line in white, or by drawing a Black box over the old text before drawing the new text in White. (to avoid using display.clearDisplay())
3) Using pixel colour inversion to remove pixels from the display (to avoid using display.clearDisplay()) (my memory is hazy on this - I would need to look at some of my old code)
4) Manually calling display.display() only when absolutely needed.
5) Automatically calling display.display() using the metro library every 0.1 seconds when I need animations.

There are possibly other tools/arguments in i2c_t3 library to speed things up but I did not need any more improvement so did not dig deeper.
 
... because it's managing MIDI delays ( each entering note, sometimes 6 notes/bar at 300BPM, generates 4 delays )...
I think we need to understand this part as I don't think it's a standard thing in MIDI so perhaps the solution is in managing updating the screen around what else you are doing.
 
Simplest way to speed up the program is only updating the display a certain number of times per second instead of once per loop using a timer. So instead of updating your display 10,000 times a second you can do something reasonable, like 30 or 60 times depending on what you prefer.
If you are trying to update in the loop then that is the problem...

The elapsedMillis() function is super easy to use to get something to run at set intervals.
https://www.pjrc.com/teensy/td_timing_elaspedMillis.html
 
Sounds interesting. I think it all comes down to the numbers you are getting and the numbers you need. If the problem is that writing to the display takes too long, then you might be able to partition that process.

In one of my projects, I write text / draw pixels to an off-screen buffer. When ready, I copy the pixel data via SPI to the display. I use an interruptible update function that passes a reference to a function to be called periodically. The callback function takes care of serial processing and button presses etc., so that the system remains responsive. The update function takes about 4200 micro-seconds to complete, but allows the system to remain responsive to within a millisecond.

If you really need full processor attention for the midi work, perhaps you could upgrade the midi stuff to a T4 and have it pass the data to be displayed via Serial to another Teensy (3.5) that handles display and possibly some other tasks?
 
you could consider using something like a Teensy LC to control the display, and have the main teensy communicate to the LC what to write, and let the LC deal with menus, redisplay, etc. .

interesting option : could they simply communicate through the serial Rx/Tx ? i've never done that before. are there some important things to know before jumpint into this option ?
 
I think we need to understand this part as I don't think it's a standard thing in MIDI so perhaps the solution is in managing updating the screen around what else you are doing.

it is standard MIDI, but pushed to its limit speed, so when a very small delay arises, it's audible, as some kind of glitch. the whole calculation and MIDI note on/off is fast, but updating the screen ( even a single digit ) is comparatively very slow.
 
If you really need full processor attention for the midi work, perhaps you could upgrade the midi stuff to a T4 and have it pass the data to be displayed via Serial to another Teensy (3.5) that handles display and possibly some other tasks?

sounds right. T3.5 is fast enough for the MIDI calculations, when now hindered by printing on the screen, one main loop takes abou 70-80 microseconds.
do you have any good examples on combining 2 Teensy ? as i asked above, simple serial communication ?
 
do you have any good examples on combining 2 Teensy ? as i asked above, simple serial communication ?

I don't. At least not yet. I think the way to go with inter processor communication is to use the CAN bus. That allows you to send small packets of data between chips at high speed with error detection.

Speeds of up to 60MHz have been quoted in other threads: https://forum.pjrc.com/threads/56035-FlexCAN_T4-FlexCAN-for-Teensy-4

Just 2 pins are required, leaving the I2C, SPI and auto pins free for other work. The nice thing about serial communication in this way is that you don't have to wait for it to complete. You send to data and the hardware takes care of when its ready, the main processor being free to handle the midi work. I'm excited to look into this myself, but I have a few other things to take care of first.
 
Note: I keep wondering if you might be able to simplify your stuff, if you could gain access to SPI on any of the three SPI busses of T3.5?
If so you could probably easily use one of current displays like ili9341 or ST7735, ST7789 and a few others, which some of us have made versions of the libraries that allow you to setup a frame buffer (memory) backing for the display. So you can do all of your updates, which just update memory and then when it is appropriate, you can kick off a DMA operation to update the screen. However warning DMA on T3.5 on SPI1 and SPI2 is not as nice as it is on T3.6 or T4... But it could probably easily handle the stuff in the background...
 
what about a completely different option : using a Nextion screen ? if i understand properly how it works, it should unload a lot of microcontroller work on the display...
 
what about a completely different option : using a Nextion screen ? if i understand properly how it works, it should unload a lot of microcontroller work on the display...

Gamedunio3 and Gamedunio3x are other options that have the separate microprocessor on the display, and you communicate over SPI (nextion uses a serial port)

Dig-ole also has a set of screens with a microprocessor attached. They aren't as fancy as the Nextion or Gamedunio. With dig-ole, you can use either a serial UART, I2C, or SPI:

While I've used gamedunio3 (briefly) and dig-ole, another entrant is Visic, that I have never used:

However, note I believe both Nextion and Gamedunio use TFT LCDs and not OLED displays. There are various differences between the various display technologies (angle of view, brightness, power consumption, color representation, refresh rate, etc.).

In my limited experience comparing 128x128 TFT displays to 128x128 OLED displays, I could run the TFT displays with a faster SPI bus speed than OLED. But the OLED display looked a lot better than TFT. Finding larger OLED displays might be tricky, as most of the larger OLED displays are swallowed up by cell phone manufacturers.
 
what would be the best option to minimize as much as possible the time necessary for a display ? i think i read once about OLED that had their own microcontroller to diminish the duty on the main controller.. is that an option ?
Sylvain

Best would be to update the display only if there is free CPU time. Now it takes to look at the code to find out, where and when in the loop this could be.
Think of the loop as a "frame". I've made a controller with T3.6 that reads several sensors each frame and can still update a SSD1306 OLED without any noticable intereference to a 2k frame rate, just because updates to the display are scheduled on a buffer and only then sorted out and written to the display, if there is cpu time available. This requires adding some scheduling logic to the code, avoiding any locking situations like "while (waitforinput)", etc ,by using polling and state switching.
 
Again we have spi displays that support doing their updates in background using dma.

Maybe at some point might try to do same for I2C display, if it would make sense...

I think at this point, to give any relevant recommendations you may need to give more specific information on you sketch, like what display you are using and what library, and actual flow of the code...

Also what things you have now tried.
 
If i recall what we've done on a 1MHz 6510 CPU, like synthetic h-blank video graphics while running a functional game or demo, with sound and animation and disk access, then i can just laugh at the fact, that people are now calling for a co-processor to update a tiny OLED display connected to a several hundred times stronger Teensy. Ever thought about that? Ever thought about real coding? Sorry to be so direct, but where is IT heading at?
 
Also what things you have now tried.

ok, some time later, here is what i did :

- tried one Nextion screen, beautiful design and wonderful for the eyes, but not really solving the problem as transmission speed was still much slowing down the problem

- finally did some coding ( no idea if it was "real coding" as mentionned above, or my usual naïve coding, but well, it worked ) :
considering i could only update regularly one character, to avoid big lines of characters slowing down the sketch, i created a first buffer where stuff TO BE written is loaded ; a second one containing what HAS BEEN written is contained.
the routines to print on screen are writing on the first buffer, and every 15 msec there is a comparison between the two buffers ; the first difference spotted is written to the screen and the 2nd buffer is updated.
this allows NOT TO write stuff that would be overwritten very quickly ( this is what happens when a menu-knob is turned quickly, it used to send a lot of text to be printed to the screen )

here are the most important parts, to show how it works. comments are welcome ! ( please remember i'm not an IT guy *AT ALL*, so, have mercy.. )

Code:
IntervalTimer myTimerScreen ; 

const byte sizeX = 16; 
const byte sizeY = 8 ;  
const int tailleEcran = (int) sizeX * (int) sizeY ; 

char bufferWrite[sizeX][sizeY] = {0}; 
char bufferDisplay[sizeX][sizeY] = {0}; 

byte cursorX = 0 ;
byte cursorY = 0 ; 

bool readyToUpdateScreen = false;

void updateScreen(void)
{
    readyToUpdateScreen = true;
}

void buffer_setCursor(byte x, byte y)
{
	cursorX = x;
	cursorY = y;
}

void buffer_print(String Texte)
{
	int positionBuffer = cursorY * sizeX + cursorX;

	byte longueur = Texte.length();

	for (int i = 0; i < longueur; i++)
	{
		bufferWrite[positionBuffer%sizeX][positionBuffer/sizeX] = (char)Texte[i];
		positionBuffer++ ; 
	}

	cursorX = positionBuffer % sizeX;
	cursorY = positionBuffer / sizeX;
}

void setup()
{
    myTimerScreen.begin(updateScreen,15000);  
}

void loop()
{

// HERE GOES ALL THE CODE MANAGING ROTARY ENCODERS, MIDI IN/OUT, SENDING TEXT TO DISPLAY, etc.

// SCREEN UPDATE 

    bool copyScreen = false;

    noInterrupts();
       copyScreen = readyToUpdateScreen;
       readyToUpdateScreen = false;
    interrupts();

    if (copyScreen == true )
    {
           int choixBuffer = 0 ; // point de départ nul 

           for ( int t = 0 ; t < tailleEcran ; t++ )
           {
                    int u = ( choixBuffer + t ) % tailleEcran ; 
                    int newX = u % sizeX ; 
                    int newY = u / sizeX ;

                    if (bufferWrite[newX][newY] != bufferDisplay[newX][newY])
                    {
                               u8x8.setCursor(newX,newY);
                               u8x8.print(bufferWrite[newX][newY]);
      
                              bufferDisplay[newX][newY] = bufferWrite[newX][newY] ; 
                              t = tailleEcran ; 
                     }
          }
    }
}
 
If i recall what we've done on a 1MHz 6510 CPU, like synthetic h-blank video graphics while running a functional game or demo, with sound and animation and disk access, then i can just laugh at the fact, that people are now calling for a co-processor to update a tiny OLED display connected to a several hundred times stronger Teensy. Ever thought about that? Ever thought about real coding? Sorry to be so direct, but where is IT heading at?

No problem, use the ili9341 in DMA mode.

 
Status
Not open for further replies.
Back
Top