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

