{SOLVED} Atmega644 with SSD1306 I2C 128x64 communication troubles

Hi there!

I've got in trouble for some time now before coming here asking for help!

Basically I experiment with AVR's and got a atmega644 running on a breadboard at it's factory clock 1MHz.

I have an SSD1306 128x64 oled screen connected by SDA and SDL on the atmega.

First I ran an I2C scanner sketch and I've got a device at 0x3C.

Then I uploaded the exemples sketch from adafruit ssd1306 library with the correct changes on address and with the MyghtyCore optibootloader with the right settings and "standard" pinout with the pickit2 thru avrdudess 2.11.

All the stuff involved other than controlling the SSD1306 where tested before coming at this point.

So far it's clear that the device is beeing recognized on the I2C bus but nothing happens after uploading the sketch.

Have somebody any suggestions for making this work?

Maybe it's an issue with clock settings... should I run the I2C bus slower? How should I do that?

Thank's for suggestions!

Not sure about the Adafruit library, but mine will work with your setup. You can either use the ATmega's I2C hardware or pick any 2 GPIO pins to be SDA/SCL. The speed is not the issue. SSD1306 I2C displays can handle an I2C clock from 0 to 1.6Mhz. Even if the Arduino pin mapping you're using is incorrect, my code can still work. Install these libraries from the library manager:

BitBang_I2C
ss_oled

For the init function, specify the pin numbers of the SCL/SDA pins you want to use. To use the fast version of my IO code, specify the port and bit. e.g. if SDA is connected to PORTB bit 3, then pass 0xB3 as the pin number to the init function.

Thank’s Mr. Optimisation for the great work!

So I tried your “simple example” code with no luck.

I put 0xC1 for SDA on PC1 and 0xC0 for SCL at PC0 according to this pinout.

Here’s the code I uploaded:

//
// Small Simple OLED library demo for AVR platform,
// without line drawing function (saving of 1K RAM)
//
#include <ss_oled.h>

void setup()
{
    int rc;
    rc = oledInit(OLED_128x64, 0, 0, 0xC1, 0xC0, 400000L);       // Standard HW I2C bus at 400Khz

    if (rc != OLED_NOT_FOUND)
    {
        char *msgs[] =
        {
          "SSD1306 @ 0x3C",
          "SSD1306 @ 0x3D",
          "SH1106 @ 0x3C",
          "SH1106 @ 0x3D"
        };

        oledFill(0, 1);
        oledWriteString(0, 0, 0, (char *)"OLED found:", FONT_NORMAL, 0, 1);
        oledWriteString(0, 10, 2, msgs[rc], FONT_NORMAL, 0, 1);
        delay(3000);
    }
}

void loop()
{
    int i, x, y;

    oledFill(0, 1);
    oledWriteString(0, 16, 0,(char *)"ss_oled Demo", FONT_NORMAL, 0, 1);
    oledWriteString(0, 0, 1,(char *)"Written by Larry Bank", FONT_SMALL, 1, 1);
    oledWriteString(0, 0, 3,(char *)"**Demo**", FONT_STRETCHED, 0, 1);
    oledWriteString(0, 9, 6,(char *)"for AVR", FONT_STRETCHED, 0, 1);

    delay(2000);
    oledFill(0, 1);

    for (i = 0; i < 1000; i++)
    {
        x = random(128);
        y = random(64);
        oledSetPixel(x, y, 1, 1);
    }

    delay(2000);
}

I see no data or clock on those pins with my oscilloscope hooked up.

Is my pin addressing right?

Ganjouille:
I see no data or clock on those pins with my oscilloscope hooked up.

Is my pin addressing right?

The code looks correct for those port/pin numbers. Are you sure your display is connected to PC1 and PC0?

Yes, still detected with i2c scanner... so assume it's correct.

I get this warning, ignored it for the moment...

Compiling libraries...
Compiling library "Wire"
"C:\\Program Files (x86)\\Arduino\\hardware\\tools\\avr/bin/avr-g++" -c -g -Os -Wall -Wextra -std=gnu++11 -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega644p -DF_CPU=1000000L -DARDUINO=10810 -DARDUINO_AVR_ATmega644 -DARDUINO_ARCH_AVR "-IC:\\Users\\Molecular Ignition\\AppData\\Local\\Arduino15\\packages\\MightyCore\\hardware\\avr\\2.0.3\\cores\\MCUdude_corefiles" "-IC:\\Users\\Molecular Ignition\\AppData\\Local\\Arduino15\\packages\\MightyCore\\hardware\\avr\\2.0.3\\variants\\standard" "-IC:\\Users\\Molecular Ignition\\AppData\\Local\\Arduino15\\packages\\MightyCore\\hardware\\avr\\2.0.3\\libraries\\Wire\\src" "C:\\Users\\Molecular Ignition\\AppData\\Local\\Arduino15\\packages\\MightyCore\\hardware\\avr\\2.0.3\\libraries\\Wire\\src\\Wire.cpp" -o "G:\\TEMP\\arduino_build_945948\\libraries\\Wire\\Wire.cpp.o"
"C:\\Program Files (x86)\\Arduino\\hardware\\tools\\avr/bin/avr-gcc" -c -g -Os -Wall -Wextra -std=gnu11 -ffunction-sections -fdata-sections -MMD -mmcu=atmega644p -DF_CPU=1000000L -DARDUINO=10810 -DARDUINO_AVR_ATmega644 -DARDUINO_ARCH_AVR "-IC:\\Users\\Molecular Ignition\\AppData\\Local\\Arduino15\\packages\\MightyCore\\hardware\\avr\\2.0.3\\cores\\MCUdude_corefiles" "-IC:\\Users\\Molecular Ignition\\AppData\\Local\\Arduino15\\packages\\MightyCore\\hardware\\avr\\2.0.3\\variants\\standard" "-IC:\\Users\\Molecular Ignition\\AppData\\Local\\Arduino15\\packages\\MightyCore\\hardware\\avr\\2.0.3\\libraries\\Wire\\src" "C:\\Users\\Molecular Ignition\\AppData\\Local\\Arduino15\\packages\\MightyCore\\hardware\\avr\\2.0.3\\libraries\\Wire\\src\\utility\\twi.c" -o "G:\\TEMP\\arduino_build_945948\\libraries\\Wire\\utility\\twi.c.o"
C:\Users\Molecular Ignition\AppData\Local\Arduino15\packages\MightyCore\hardware\avr\2.0.3\libraries\Wire\src\utility\twi.c: In function '__vector_26':

C:\Users\Molecular Ignition\AppData\Local\Arduino15\packages\MightyCore\hardware\avr\2.0.3\libraries\Wire\src\utility\twi.c:431:49: warning: this statement may fall through [-Wimplicit-fallthrough=]

       twi_masterBuffer[twi_masterBufferIndex++] = TWDR;

                                                 ^

C:\Users\Molecular Ignition\AppData\Local\Arduino15\packages\MightyCore\hardware\avr\2.0.3\libraries\Wire\src\utility\twi.c:432:5: note: here

     case TW_MR_SLA_ACK:  // address sent, ack received

     ^~~~

C:\Users\Molecular Ignition\AppData\Local\Arduino15\packages\MightyCore\hardware\avr\2.0.3\libraries\Wire\src\utility\twi.c:513:9: warning: this statement may fall through [-Wimplicit-fallthrough=]

       if(0 == twi_txBufferLength){

         ^

C:\Users\Molecular Ignition\AppData\Local\Arduino15\packages\MightyCore\hardware\avr\2.0.3\libraries\Wire\src\utility\twi.c:518:5: note: here

     case TW_ST_DATA_ACK: // byte sent, ack returned

     ^~~~

Compiling core...
Using precompiled core: G:\TEMP\arduino_cache_66726\core\core_78a141256b9650f62e1df289c3fb4759.a
Linking everything together...[code/]

Dont know what it means...

I also tried youre I2C_Detector with pins 0xC0, C1 and 16, 17 but I see no data farames with my oscilloscope.

But I do see the frames with standard wire library so now I try build up a drawing sequence from within programm.

Not sure what to tell you, but the ss_oled library (using hardware I2C and bit banging) has worked on every single embedded board I own - AVR (many types including ATmega644 and ATmega1284), ARM (many types), ESP32 (many types), ESP8266 (many types).

ok, so it sould work straight... do you programm them with arduino? witch library/bootloader? MightyCore? Or you write you're own? (:

Thank's anyway I will make it work! (:

Ok I’ve got something out of it. But it’s really slow! Why this?

It was the reset pin that need to get pulled low once each powering cycle…

I ported the commands from another library for SH1106. Here’s the code:

#include <Wire.h>

const byte imgSmiley[] PROGMEM = {
  21, // width
  3, // pages
  4,0,192,48,8,130,4,130,130,133,1,130,130,130,4,7,8,48,192,0,31,96,128,130,0,1,67,130,132,1,3,131,0,
  1,3,130,132,1,67,130,0,3,128,96,31,130,0,2,1,2,130,4,130,8,133,17,130,8,130,4,2,2,1,130,0};
  
bool BoldSH1106 = false;

const int colOffset = 0;
const int addr = 0x3C;

//==============================================================
// DrawByteSH1106
//   draws a byte at x,page
//   sets up the row and column then sends the byte
//   quite slow
//==============================================================
void DrawByteSH1106(byte x, byte page, byte i) {
  setupPageCol(page, x);
  Wire.write(i);
  Wire.endTransmission();
}

//==============================================================
// setupPageCol
//   sets up the row and column 
//   then gets ready to send one or more bytes
//   should be followed by endTransmission
//==============================================================
void setupPageCol(int page, int col) {
  col += colOffset;
  Wire.beginTransmission(addr);
  Wire.write(0x00); // the following bytes are commands
  Wire.write(0xB0 + page); // set page
  Wire.write(0x00+(col & 15)); // lower columns address
  Wire.write(0x10+(col >> 4)); // upper columns address
  Wire.endTransmission();

  Wire.beginTransmission(addr);
  Wire.write(0x40); // the following bytes are data
}

//==============================================================
// setupCol
//   sets up the column
//==============================================================
void setupCol(int col) {
  col += colOffset;
  Wire.beginTransmission(addr);
  Wire.write(0x00); // the following bytes are commands
  Wire.write(0x00+(col & 15)); // lower columns address
  Wire.write(0x10+(col >> 4)); // upper columns address
  Wire.endTransmission();
}

//==============================================================
// setupPage
//   sets up the page
//==============================================================
void setupPage(int page) {
  Wire.beginTransmission(addr);
  Wire.write(0x00); // the following bytes are commands
  Wire.write(0xB0 + page); // set page
  Wire.endTransmission();
}

//==============================================================
// clearSH1106
//   fills the screen with zeros
//==============================================================
void clearSH1106() {
int x,p;
const int n = 26;
  for(p = 0; p <= 7; p++)
    for(x = 0; x <= 127; x++) {
      if (x%n == 0) setupPageCol(p, x);
      Wire.write(0);
      if ((x%n == n-1) || (x == 127)) Wire.endTransmission();
    }
}

//==============================================================
// initSH1106
//   initialises the SH1106 registers
//==============================================================
void initSH1106() {
  Wire.endTransmission();
  Wire.beginTransmission(addr);
  Wire.write(0x00); // the following bytes are commands
  Wire.write(0xAE); // display off
  Wire.write(0xD5); Wire.write(0x80); // clock divider
  Wire.write(0xA8); Wire.write(0x3F); // multiplex ratio (height - 1)
  Wire.write(0xD3); Wire.write(0x00); // no display offset
  Wire.write(0x40); // start line address=0
  Wire.write(0x33); // charge pump max
  Wire.write(0x8D); Wire.write(0x14); // enable charge pump
  Wire.write(0x20); Wire.write(0x00); // memory adressing mode=horizontal (only for 1306??) maybe 0x00
  Wire.write(0xA1); // segment remapping mode
  Wire.write(0xC8); // COM output scan direction
  Wire.write(0xDA); Wire.write(0x12);   // com pins hardware configuration
  Wire.write(0x81); Wire.write(0xFF); // contrast control // could be 0x81
  Wire.write(0xD9); Wire.write(0xF1); // pre-charge period or 0x22
  Wire.write(0xDB); Wire.write(0x40); // set vcomh deselect level or 0x20
  Wire.write(0xA4); // output RAM to display
  Wire.write(0xA6); // display mode A6=normal, A7=inverse
  Wire.write(0x2E); // stop scrolling
  Wire.write(0xAF); // display on
  Wire.endTransmission();
  
  clearSH1106();
}

//==============================================================
// DrawBarSH1106
//   draws a single byte
//   a 'bar' is a byte on the screen - a col of 8 pix
//   assumes you've set up the page and col
//==============================================================
void DrawBarSH1106(byte bar) {
  Wire.beginTransmission(addr);
  Wire.write(0x40); // the following bytes are data
  Wire.write(bar);
  Wire.endTransmission();
}
  
//==============================================================
// DrawImageSH1106 
//   at x,p*8
//   unpacks RLE and draws it
//   returns width of image
//==============================================================
int DrawImageSH1106(int16_t x, int16_t p, const uint8_t *bitmap) {
  #define DrawImageSH1106Bar {\
    if ((page >= 0) && (page <= 7) && (ax >= 0) && (ax <= 127)) {\
      n++;\
      if ((page != curpage) || (n > 25)){\
        if (curpage >= 0) \
          Wire.endTransmission();\  
        setupPageCol(page, ax);\
        curpage = page;\
        n = 0;\
      }\
      Wire.write(bar);\
    }  \
    ax++;\
    if (ax > x+wid-1) {\
      page++;\
      ax = x;\
    }\
  }

  int wid,pages,j,k,page,ax,bar,curpage,n;

  wid = pgm_read_byte(bitmap++);
  pages = pgm_read_byte(bitmap++);

  page = p;
  ax = x;
  curpage = -1;

  while (page <= p+pages-1) {
    j = pgm_read_byte(bitmap++);
    if (j > 127) {
      for (k = 129;k<=j;k++) {
        bar = pgm_read_byte(bitmap);
        DrawImageSH1106Bar  
      }
      bitmap++;
    } else {
      for (k = 1;k<=j;k++) {
        bar = pgm_read_byte(bitmap++);
        DrawImageSH1106Bar
      }
    }
  }
  Wire.endTransmission();

  return wid;
}

void setup() {

  Wire.begin();
  initSH1106();
  DrawImageSH1106(0, 0, imgSmiley);
}

void loop() {
  // put your main code here, to run repeatedly:

}[code/]

That code looks to be very inefficient and the default I2C speed is 100Kbs, so those 2 factors will make it slow. My library detects SSD1306 vs SH1106 automatically (along with the I2C address).

Ok wow I hooked it up with youre library and it's working very well! Great job!

Thanks a lot!

Ganjouille:
Ok wow I hooked it up with youre library and it's working very well! Great job!

Thanks a lot!

Glad you got it working. Since the ATmega644 has plenty of FLASH and enough RAM, you can comment out the #ifndef AVR parts in the code to enable the back buffer and large font. This will give you the full functionality including line drawing and delayed rendering.