I2C Adafruit_SSD1306 OLED too slow with 8MHz Arduino on a breadboard

Hello, folks.

I am porting an old project from a 16MHz Nano to an 8MHz ATMega328P on a breadboard and just hit an odd behaviour on everything related to time (I guess), but that compromises OLED performance most.

I am using Adafruit_SSD1306 lib latest (2.5.1) and everything looks fines except everything is slow, like in half speed. Did some dirty check on I2C clock and it looks low (around 35KHz).

A double checked on lfuse value returns 0xE2, giving us 8MHz clk with 1:1 prescaler (CKDIV8 unprogrammed). So it looks alright.

I also loaded a clean code to test the OLED alone with the same poor speed.

As stated by others in the forum, I'd tried to fiddle with the TWFR values but it seems not straight forward because of the new SSD1306 constructors.

My sketch includes are as follows:

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EasyButton.h>
#include <ArduinoSort.h>
#include <EEPROM.h>

Any clue or advice?!

Thanks in advance!

Are you using the graphics? If not try the SSD1306Ascii library by Bill Geirman. It is only character based but reasonably fast in an 8Meg.

I don't use the adafruit GFX but I would guess it is written to go as fast as the processor will allow. Now you are using an 8Mhz clock vs a 16Mhz clock, so running at 1/2 the speed is expected.

You will find the OLED is not the problem but the code is taking twice as long. Your problem matches the clock change.

Thanks for your tips @JohnRob.

You are right about Adafruit's been written to top speed. All specs, lib and M328 points it is OK to run I2C @ 400KHz with a 8MHz cpu_clk. This is what every documentation I've found states.

I am fiddling some simple benchmarks and should return soon.

Hi there, @gilshultz. That sure makes sense but let me share some thoughts.

I've just checked a weird behaviour. Take a look at this snippet and comments:

// Benchmark OLED write time and delay time
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.print(F("Clear: "));
  display.println(millis()); // our t0 ref; got ~54
  display.display();
  delay(500);
  display.print(F("delay: ")); 
  display.println(millis());
  display.println(F("Wait 10 secs!"));
  display.display(); // shows something near a 500ms eval time (~593)
  for (byte i=0; i<10; i++) { // now, this loop is showing a 2x timing issue
    display.print(F("."));
    display.display();
    delay(1000);
  } // should print 10 dots in 10 secs, but it takes 20!!! I measured with a chronometer oO

So, I do believe there is some hw/sw issue related to the cpu_clk being 8MHz and I2C/delay functions behaviour. Seem that invoking display and looping affects the delay function somehow.

Have you told the Arduino IDE that your using 8Mhz ?

Does the Blink example flash an LED at the normal rate ?

1 Like

Hi, @srnet.

I was using VSCode w Arduino ext to upload my code and after your question did some checks:

  1. VSCode is using board Arduino Nano w/ M328 @ 16MHz);
  2. Code is programmed with "Upload using programmer" command;
  3. In this context we get the behaviour I shared here with the 10sec loop taking double time;

So, moving to Arduino IDE, could select Arduino on a Breadboard M328 8MHz trying to follow your clues, and now we have a twist as OLED performance is terrible but the loop runs at the correct speed and completes in 10 secs o.O

One important remark here is that I am using no bootloader and upload directly through Arduino as ISP programmer.

Here's the content of boards.txt file for the selected board/processor in Arduino IDE:

atmega328bb.name=ATmega328 on a breadboard (8 MHz internal clock)

atmega328bb.upload.protocol=arduino
atmega328bb.upload.maximum_size=30720
atmega328bb.upload.speed=57600

atmega328bb.bootloader.low_fuses=0xE2
atmega328bb.bootloader.high_fuses=0xDA
atmega328bb.bootloader.extended_fuses=0x05

atmega328bb.bootloader.file=atmega/ATmegaBOOT_168_atmega328_pro_8MHz.hex
atmega328bb.bootloader.unlock_bits=0x3F
atmega328bb.bootloader.lock_bits=0x0F

atmega328bb.build.mcu=atmega328p
atmega328bb.build.f_cpu=8000000L
atmega328bb.build.core=arduino:arduino
atmega328bb.build.variant=arduino:standard
atmega328bb.build.board=AVR_ATMEGA328BB

atmega328bb.bootloader.tool=arduino:avrdude
atmega328bb.upload.tool=arduino:avrdude

So, I guess I've got two issues to resolve:
a) why this happens even when using Arduino IDE where all recommended settings are available;
b) why VSCode Arduino cannot find the M328BB hardware options, even sourcing the Arduino IDE paths;

For now, let's focus on item "a" and get timing right.

Thanks for your mighty contribution.

Have you burned the 8MHz bootloader to set the fuses properly?

I had the fuses set up using avrdude:
lfuse = 0xE2
hfuse = 0xDA
efuse = xFF

I am not using any bootloader. Sketch gets uploaded using ISP programmer.

Doing a full env reinstall to shake all libs fresh.

If you're not using a bootloader, you should probably set hfuse to 0xDB rather than 0xDA.

But I don't think that should affect the timing. :frowning:

How much dynamic memory use does the IDE show when compiling?

Do you see any random pixels in the corner of the display, which can indicate the stack is overlapping the display buffer.

I double checked the docs and you could be right. 0xDB would set minimum boot size.

But I was thinking, what about use 0xD9 and just never use any reset vector?
I imagine it should mean boot size "zero".

Agreed that this will not affect timing, but was useful anyway.

Update: changed hfuse to 0xD9 with no harm.

It shows 1811 bytes used. And I notice no random pixels.
Also I have very few nested function calls, say, three levels deep but pass no params. So stack is not abused. Most of function calls are one level.

The sketch animates a splash screen (64x64) and then all display activity is text based.

Update: sorry about typo in this response! Sketch uses 811 bytes RAM

How big is the display, 128x32 or 128x64? Adafruit's library allocates the display buffer at runtime.

Display is 128x64.
All static data in sketch are using PROGMEM.

Adafruit says it need 1K for framebuffer.
So I believe 811 + 1024 fits the M328 2K RAM size.
Is that ok?

The should be enough memory.

Hello, folks.

I found the solution!
After checking the Arduino IDE end-to-end I noticed I was using Adafruit SSD1306 latest version (2.5.1) but the Arduino core libs where outdated. So I decided to give it a kick and installed all new from scratch and voilá: timing is all good, even at 8MHz the display is smooth both on splash animation and text performance. My rational is that the latest SSD_1316 lib depends on recent Wiring versions and they probably were having an argument =]

Still can't select the M328 Breadboard option in VSCode but it is ok to use Arduino IDE until I get this issue resolved.

Thank you all for your contributions that helped me find the solution and tune the sketch up.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.