OLED display (0.96 inch) doesn't work with standalone ATmega328pu


I am making a pcb that has rotary encoder and oled 128x64 pixels with bare atmega328p but it seems there is something wrong with the schematic design (oled still black after powerup but when connecting it to normal arduino uno dev board with same schematic it works). Below image shows the schematic. Thanks in advance.

Does the OLED have (internal) pullup resistors? Pull Up Resistors | Working with I2C Devices | Adafruit Learning System

Thanks for the reply,
What value should I use for the pull-up resistor if it doesn’t have one?”

30 second glance:

No ground on 328P's pin 22.

No 0.1uF decoupling cap on 328P.

No 0.1uF cap from Aref to ground.

No sketch.

No fuses setting shown.

My sketch:

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// Rotary encoder pins
const int ENC_SW  = 2;
const int ENC_DT  = 3;
const int ENC_CLK = 4;

// Relay
const int RELAY_PIN = 5;

// Timer states
enum State { SET_MINUTES, SET_SECONDS, RUNNING, FINISHED };
State currentState = SET_MINUTES;

int minutes = 0;
int seconds = 0;

// Encoder
int lastStateCLK;
bool buttonPressed = false;
unsigned long lastButtonTime = 0;
const unsigned long BTN_DEBOUNCE = 200;

unsigned long previousMillis = 0;

// =========================
// SETUP
// =========================
void setup() {
  pinMode(ENC_SW, INPUT_PULLUP);
  pinMode(ENC_DT, INPUT_PULLUP);
  pinMode(ENC_CLK, INPUT_PULLUP);

  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, LOW);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.display();

  lastStateCLK = digitalRead(ENC_CLK);

  showMenu();
}

// =========================
// MAIN LOOP
// =========================
void loop() {
  handleButton();
  handleEncoder();

  // RUNNING TIMER
  if (currentState == RUNNING) {
    unsigned long now = millis();
    if (now - previousMillis >= 1000) {
      previousMillis = now;

      if (seconds > 0) seconds--;
      else if (minutes > 0) {
        minutes--;
        seconds = 59;
      } else {
        currentState = FINISHED;
        digitalWrite(RELAY_PIN, LOW);
        showFinished();
        return;
      }

      showTimer();
    }
    digitalWrite(RELAY_PIN, HIGH);
  }
}

// =========================
// HANDLE ROTARY ENCODER (quadrature decoding)
// =========================
void handleEncoder() {
  int currentCLK = digitalRead(ENC_CLK);
  int currentDT  = digitalRead(ENC_DT);

  // Only act on falling edge of CLK
  if (currentCLK != lastStateCLK && currentCLK == LOW) {
    if (currentDT != currentCLK) { // clockwise
      incrementTime(1);
    } else {                       // counter-clockwise
      incrementTime(-1);
    }
  }

  lastStateCLK = currentCLK;
}

void incrementTime(int delta) {
  if (currentState == SET_MINUTES) {
    minutes = constrain(minutes + delta, 0, 99);
    showMenu();
  } else if (currentState == SET_SECONDS) {
    seconds = constrain(seconds + delta, 0, 59);
    showMenu();
  }
}

// =========================
// BUTTON HANDLER
// =========================
void handleButton() {
  if (digitalRead(ENC_SW) == LOW) {
    if (!buttonPressed && millis() - lastButtonTime > BTN_DEBOUNCE) {
      buttonPressed = true;
      lastButtonTime = millis();
      switchState();
    }
  } else {
    buttonPressed = false;
  }
}

void switchState() {
  switch (currentState) {
    case SET_MINUTES:
      currentState = SET_SECONDS;
      showMenu();
      break;
    case SET_SECONDS:
      currentState = RUNNING;
      previousMillis = millis();
      showTimer();
      break;
    case RUNNING:
      currentState = SET_MINUTES;
      minutes = 0;
      seconds = 0;
      digitalWrite(RELAY_PIN, LOW);
      showMenu();
      break;
    case FINISHED:
      currentState = SET_MINUTES;
      minutes = 0;
      seconds = 0;
      showMenu();
      break;
  }
}

// =========================
// OLED DISPLAY FUNCTIONS
// =========================
void showMenu() {
  display.clearDisplay();
  display.setTextColor(WHITE);

  // Header
  display.setTextSize(1);
  display.setCursor(0, 0);
  if (currentState == SET_MINUTES) display.print("SET MINUTES");
  else display.print("SET SECONDS");

  char buffer[10];
  sprintf(buffer, "%02d:%02d", minutes, seconds);

  display.setTextSize(3);
  int y = 22;
  drawCenteredText(buffer, y);

  // Dynamic underline
  int16_t x1, y1;
  uint16_t w, h;
  display.getTextBounds(buffer, 0, 0, &x1, &y1, &w, &h);
  int centerX = (SCREEN_WIDTH - w) / 2;
  int charWidth = w / 5; // "MM:SS" = 5 chars

  int underlineX;
  if (currentState == SET_MINUTES) {
    underlineX = centerX;
  } else {
    underlineX = centerX + charWidth * 3;
  }
  drawUnderline(underlineX, y + 26, charWidth * 2);

  display.setTextSize(1);
  drawCenteredText("Press to Continue", 55);
  display.display();
}

void showTimer() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print("TIMER RUNNING ");
  display.print((char)16);

  char buffer[10];
  sprintf(buffer, "%02d:%02d", minutes, seconds);
  display.setTextSize(3);
  drawCenteredText(buffer, 22);

  display.setTextSize(1);
  drawCenteredText("Press to Stop", 55);

  display.display();
}

void showFinished() {
  display.clearDisplay();
  display.setTextSize(2);
  drawCenteredText("DONE", 5);

  display.setTextSize(3);
  drawCenteredText("00:00", 25);

  display.setTextSize(1);
  drawCenteredText("Press to Reset", 55);

  display.display();
}

// =========================
// HELPER FUNCTIONS
// =========================
void drawCenteredText(const char* text, int y) {
  int16_t x1, y1;
  uint16_t w, h;
  display.getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
  int x = (SCREEN_WIDTH - w) / 2;
  display.setCursor(x, y);
  display.print(text);
}

void drawUnderline(int x, int y, int length) {
  display.fillRect(x, y, length, 3, WHITE);
}

I am uploading the code through an arduino uno dev board using latest arduino ide
i added ground to pin 22 and 2 100nF caps to aref and Vcc

And the result was...?

And what are your fuse settings?

I added a 1 MΩ resistor between the crystal oscillator and it didn’t work. But when I uploaded a simple blink program on GPIO13, it worked. I’m sorry, but I don’t know what fuse settings are? I’m still a newbie.

edit:
I found this in Board.txt and I hope those relate to fuse settings for Arduino Uno:

uno.name=Arduino Uno

uno.vid.0=0x2341
uno.pid.0=0x0043
uno.vid.1=0x2341
uno.pid.1=0x0001
uno.vid.2=0x2A03
uno.pid.2=0x0043
uno.vid.3=0x2341
uno.pid.3=0x0243
uno.vid.4=0x2341
uno.pid.4=0x006A
uno.upload_port.0.vid=0x2341
uno.upload_port.0.pid=0x0043
uno.upload_port.1.vid=0x2341
uno.upload_port.1.pid=0x0001
uno.upload_port.2.vid=0x2A03
uno.upload_port.2.pid=0x0043
uno.upload_port.3.vid=0x2341
uno.upload_port.3.pid=0x0243
uno.upload_port.4.vid=0x2341
uno.upload_port.4.pid=0x006A
uno.upload_port.5.board=uno

uno.upload.tool=avrdude
uno.upload.tool.default=avrdude
uno.upload.tool.network=arduino_ota
uno.upload.protocol=arduino
uno.upload.maximum_size=32256
uno.upload.maximum_data_size=2048
uno.upload.speed=115200

uno.bootloader.tool=avrdude
uno.bootloader.tool.default=avrdude
uno.bootloader.low_fuses=0xFF
uno.bootloader.high_fuses=0xDE
uno.bootloader.extended_fuses=0xFD
uno.bootloader.unlock_bits=0x3F
uno.bootloader.lock_bits=0x0F
uno.bootloader.file=optiboot/optiboot_atmega328.hex

uno.build.mcu=atmega328p
uno.build.f_cpu=16000000L
uno.build.board=AVR_UNO
uno.build.core=arduino
uno.build.variant=standard

I tried both 10 kΩ and 3.3 kΩ I²C pull-ups, but the OLED still didn’t work. Even without any resistors, the I²C scanner code turns the LED on after I connect the OLED screen.

Code:

#include <Wire.h>

const int ledPin = 2; // LED connected to digital pin 2

void setup() {
Wire.begin();
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW); // start with LED off
}

void loop() {
byte error, address;
bool deviceFound = false;

// Scan I2C bus
for (address = 1; address < 127; address++) {
Wire.beginTransmission(address);
error = Wire.endTransmission();


if (error == 0) {
  deviceFound = true;
  break; // stop scanning after finding first device
}


}

// Turn LED on or off
if (deviceFound) {
digitalWrite(ledPin, HIGH); // device found
} else {
digitalWrite(ledPin, LOW);  // no device found
}

delay(5000); // scan every 5 seconds
}

Fuses. All those options that you set when did "Burn Bootloader" to tell the factory fresh chip what kind it was, what speed the clock was going to be, where the clock was coming from, all those things that make it work.

Like this.

You did do a "Burn Bootloader" and didn't assume that your factory fresh 328P was going to configured exactly the same as the one in your Arduino, right?

What fuse settings did you use? How did you manage to get it working?

  • If the blink sketch works, things are okay.

  • Try printing the I2C address, what is it ?

I used your sketch, as is, after properly programming the fuses in the 328P I pulled out of my parts drawer. That's how I made it work.

As it was a standalone 328P and not an Uno, I didn't use the Uno board, or the AVR core for that matter. I used MiniCore's ATmega328 board, with all its default options except choosing "No bootloader".

I imagine you could well use the Uno board and depend on its "Burn Bootloader" getting the fuses right, but I like to see what I'm getting rather than assume. Makes life easier in the long run.

  • Let’s see good images of your actual wiring.

I am using the standalone atmega328p too from the arduino uno devboard after flashing the code from the ide but it wont work.

is there any chance that the ide is not flashing the fuse settings into the bootloader of atmega328p? because when I to test the atmega328p and flash it with a simple blinking led it works just fine the only problem is turning on the oled display with the code

I am using the MiniCore core. Are you? Yes or no.

I am using the 328 (ATmega328) board from that core. Are you? Yes or no.

I did a "Burn bootloader" with all the default options except choosing "No bootloader" for the Bootloader option. Are you? Yes or no.

If any of your answers are "no", you're doing something other than what we've already established works.

  • What does this sketch example do with your circuit.
//********************************************^************************************************
//  OLED_Counter.ino
//
//  LarryD
//  Version   YY/MM/DD     Comments
//  =======   ========     ===============================================
//  1.00      22/04/21     Running code
//
//

//https://lastminuteengineers.com/oled-display-arduino-tutorial/

#include <Wire.h>
//#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

//********************************************^************************************************
#define SCREEN_WIDTH          128   //OLED display width,  in pixels
#define SCREEN_HEIGHT          64   //OLED display height, in pixels


//SSD1306 display
//size 1 is 5X7   pixels therefore, 6X8   to acount for spacing,  21 characters per line
//size 2 is 10X14 pixels therefore, 11X15 to account for spacing, 10 characters per line
//size 4 is 20X28 pixels therefore, 21X29 to account for spacing,  5 characters per line

//Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);


const byte heartbeatLED     = 13;

unsigned long counter       = 0;

//timing stuff
unsigned long heartbeatTime;
unsigned long displayTime;


//                                       s e t u p ( )
//********************************************^************************************************
//
void setup()
{
  Serial.begin(115200);

  pinMode(heartbeatLED, OUTPUT);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))
  {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }

  display.clearDisplay();

  //display.setTextColor(textColor, backgroundColor);
  display.setTextColor(WHITE, BLACK);

} //END of   setup()


//                                        l o o p ( )
//********************************************^************************************************
//
void loop()
{
  //*****************************************************              T I M E R  heartbeat 
  //is it time to toggle the heartbeatLED (every 500ms)?
  if (millis() - heartbeatTime >= 500ul)
  {
    //restart this TIMER
    heartbeatTime = millis();

    //toggle the heartbeat LED
    digitalWrite(heartbeatLED, digitalRead(heartbeatLED) == HIGH ? LOW : HIGH);
  }

  //*****************************************************              T I M E R  displayTime 
  //is time to update the display ?
  if (millis() - displayTime >= 250ul)
  {
    //restart this TIMER
    displayTime = millis();

    //clear SSD1306 display
    display.clearDisplay();

    //************************************
    display.setTextSize(1);
    display.setCursor(0, 0);

    //Size 1 line is  000000000111111111122
    //21 characters   123456789012345678901
    //Example         ..OLED Counter Demo..
    display.print("  OLED Counter Demo  ");

    //************************************
    //display.setCursor(0,9);
    //Size 1 line is  000000000111111111122
    //21 characters   123456789012345678901
    //display.print ("ABCDEFGHIJKLMNOPQRSTU");

    //************************************
    display.setTextSize(2);
    //2 = current text size, 16 is the pixel row we want to postion to
    display.setCursor(centering(counter, 2), 16);

    //Size 2 line     0000000001
    //10 characters   1234567890
    //Example           100000
    display.print(counter++);

    //************************************
    //degree symbol
    //using CP437 ASCII
    //display.cp437(true);
    //display.write(248);

    //************************************
    //  display.setTextSize(2);
    //  display.setCursor(0,16);
    //  display.setCursor(0,32);
    //  //temperature
    //  //Size 2 line is  0000000001
    //  //10 characters   1234567890
    //  //Example         ABCDEFGHIJ
    //  display.print("ABCDEFGHIJ");

    //************************************
    display.display();
  }


  //************************************
  //other non blocking code goes here
  //************************************


} //END of   loop()


//                                 c o u n t D i g i t s ( )
//********************************************^************************************************
//return the number of digits in a number
byte countDigits(int num)
{
  byte count = 0;

  while (num)
  {
    num = num / 10;
    count++;
  }

  return count;

} //END of   countDigits()


//                                    g e t D i g i t ( )
//********************************************^************************************************
//return the selected digit
byte getDigit(unsigned int number, int digit)
{
  for (int i = 0; i < digit - 1; i++)
  {
    number = number / 10;
  }

  return number % 10;

} //END of   getDigit()


//                                   c e n t e r i n g ( )
//********************************************^************************************************
//return the position to print the MSD
byte centering(unsigned long number, byte textSize)
{
  byte count = 0;
  byte charaterCellWidth = 0;

  //a basic character is 5X7, we must scale for this text size
  charaterCellWidth = (5 * textSize) + 1;

  //number of digits in our number
  while (number)
  {
    number = number / 10;
    count++;
  }

  //center location where the MSD character will be displayed
  return (SCREEN_WIDTH / 2 - (charaterCellWidth * count / 2));

} //END of   centering()


//
//********************************************^************************************************
//

i actually get it working by burning the bootloader and then flashing the sketch

thanks for the help