Hi @Fenyi,
Thank you for the suggestion to slow the I2C bus clock to address the OLED I2C pullup resistor problem. This is much better than using software I2C, which is very slow.
If one wants to use the fastest I2C speed with the OLED module and no other I2C modules, they will need to add pullup resistors to the the SDA and SCL lines. I found that 3.9K resistors allow the OLED
module to function at the default I2C bus speed.
Instead of editing the source of the u8g2 library, which will affect all uses of the library, it is preferable to set the I2C bus clock from the sketch.
We can use the .setBusClock() method, as described in the u8x8 reference.
Note that the clock rate cannot be an arbitrary value; an unsupported value will not cause an error but will use the default clock rate. (For details, refer to the ATMEGA328P documentation about the TWBR register.)
I wrote a small test program to determine the OLED update display rate for various I2C bus clock values as well as software I2C.
Results:
-
400 KHz: 66 updates/sec. Fastest
- does not work if there are no other I2C modules connected
- works with 3.9K pullup resistors on SCL and SDA. (400KHz is the default clock rate)
- 100 KHz: 33 updates/sec
- 50 KHz: 22 updates/sec.
- Software: 10 updates/sec. Slowest
My suggestions:
-
Document the OLED module problem on https://wiki.seeedstudio.com/Grove-Beginner-Kit-For-Arduino/#lesson-7-displaying-data-on-oled
-
Include instructions to add the pullup resistors if the fastest OLED display performance is desired when the other I2C modules connected.
-
Modify the OLED sketch to be sure that it works regardless of the connections of the other I2C modules. Change the setup() to include:
void setup(void) {
// slow down the I2C bus clock to enable the OLED to function
// if the other I2C modules are removed from the board;
// the default is 400000 (400 KHz)
u8x8.setBusClock(100000);
u8x8.begin();
…
Here’s a test program to compare display update speed with various clock rates:
#include <Arduino.h>
#include <U8x8lib.h>
//HW I2C:
U8X8_SSD1306_128X64_ALT0_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE); //HW I2C
// SW I2C
//U8X8_SSD1306_128X64_ALT0_SW_I2C u8x8(/* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);
unsigned long clockrates[] = { 50000L, 100000L, 400000L };
int iterations = 200;
int i;
unsigned long starttime;
int update_rate;
int prev_update_rate = 0;
void setup(void) {
// slow down the I2C bus clock to enable the OLED to function
// if the other I2C modules are removed from the board;
// the default is 400000 (400 KHz)
u8x8.setBusClock(100000);
u8x8.begin();
u8x8.setFlipMode(1);
u8x8.setFont(u8x8_font_chroma48medium8_r);
Serial.begin(9600);
}
void loop(void) {
Serial.println("OLED I2C bus clock test");
Serial.println("");
for (int c = 0; c < sizeof(clockrates) / sizeof(clockrates[0]); c++) {
Serial.print("Clock rate: ");
Serial.println(clockrates[c]);
u8x8.setBusClock(clockrates[c]);
u8x8.begin();
u8x8.setFlipMode(1);
u8x8.setFont(u8x8_font_chroma48medium8_r);
starttime = millis();
u8x8.clear();
for (i = 1; i < iterations; i++) {
update_rate = i / ((millis() - starttime) / 1000); //number of display updates per second
u8x8.setCursor(0, 0);
u8x8.print(clockrates[c]);
u8x8.setCursor(0, 1);
u8x8.print(i);
if (update_rate != prev_update_rate) {
u8x8.clearLine(2);
u8x8.setCursor(0, 2);
u8x8.print(update_rate);
u8x8.setCursor(5, 2);
u8x8.print("/sec");
prev_update_rate = update_rate;
}
delay(1);
}
Serial.print(update_rate);
Serial.println(" updates/second");
Serial.println("");
}
}
Thanks,
Wayne