Weird issue with Nano clone + 128x32 OLED [SOLVED]

Hello,

I have a weird issue with a Nano clone (CH340, not FTDI) and one of these I²C 128x32 OLED, driven with u8g2...

Until now, I've been using some OLEDs, only with STM32s (BluePill and BlackPill, genuine MCUs and chinese clones), without a problem. Today, I needed to quickly make a pulse generator, and a Nano was ideal for this project (thanks to Paul Stoffregen for TimerOne and MsTimer2 libraries that made things as easy as writing an #include !).

But, everytime the nano and its display are powered THROUGHT THE USB PORT, it reboots on and on ! Removing the display, or not initializing u8g2 solves the problem. But I have no display :grin: Not really a solution...

I took a ProMini that I had in the drawers, and flashed it using a (genuine) Arduino FTDI adapter, and no problem. No reboots.

I think the problem is hardware related : is there a known issue with CH340 Nanos and I²C ?

Not a huge issue for me, as the circuit is powered by an external PSU ; but I'd like to know !

The code is pretty simple and straightforward (who said quick and dirty ??? I can hear you !):

[EDIT]

It seems I found a problem in the code while editing this message. Will update in a few moments !

(there were errors in the comments)

/*
 RPM generator
*/


#include <MsTimer2.h>
#include <ClickEncoder.h>
#include <TimerOne.h>
#include <Wire.h>
#include <SPI.h>
#include <U8x8lib.h>
#include <U8g2lib.h>

//#define __SERIAL_DEBUG

#define ENC_A 4
#define ENC_B 3
#define ENC_BTN 2
#define OUT_PIN 9

/*
 SCK A5
 SDA A6
*/

#define TIMER1_SIGNAL_PERIOD 1000 // 1000 microseconds
#define TIMER2_ENCODER_PERIOD 1 // 1 millisecond

U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C oled(U8G2_R0); // full buffer

ClickEncoder encoder(ENC_A, ENC_B, ENC_BTN);

void encoderIsr()
{
 encoder.service();
}

void setup(void)
{
#ifdef __SERIAL_DEBUG
 Serial.begin(115200);
 Serial.println("Hello !");
#endif

 pinMode(LED_BUILTIN, OUTPUT);

 // encoder
 pinMode(ENC_BTN, INPUT_PULLUP);
 pinMode(ENC_A, INPUT_PULLUP);
 pinMode(ENC_B, INPUT_PULLUP);
 encoder.setDoubleClickEnabled(false);
 encoder.setButtonHeldEnabled(false);
 encoder.setAccelerationEnabled(false);

 // encoder timer
 MsTimer2::set(TIMER2_ENCODER_PERIOD, encoderIsr); // 1 millisecond
 MsTimer2::start();

 // output
 pinMode(OUT_PIN, OUTPUT);
 Timer1.setPeriod(TIMER1_SIGNAL_PERIOD); // 1000 microseconds
 Timer1.pwm(OUT_PIN, 100);
 Timer1.initialize(4);  // 40 us = 25 kHz

 // display
 oled.begin();
 oled.setContrast(1);
 oled.setFont(u8g2_font_6x10_tf);
 oled.clear();
 delay(1000);
}

uint32_t rpm = 0;
uint32_t rpmOld = -1;
int16_t encPos = 0;
int16_t encPosOld = -1;

void loop(void)
{
 uint8_t buttonState = encoder.getButton();

#ifdef __SERIAL_DEBUG
 if (buttonState != 0)
 Serial.println(buttonState);
#endif

 if (buttonState != 0 && buttonState == ClickEncoder::Clicked)
 rpm = 1500;

 encPos += encoder.getValue();
 int16_t delta = encPos - encPosOld;
 encPosOld = encPos;

 if (delta > 0 && rpm < 500000)
 {
 if (rpm >= 100000)
 rpm += 10000;
 else if (rpm >= 50000)
 rpm += 5000;
 else if (rpm >= 10000)
 rpm += 1000;
 else if (rpm >= 5000)
 rpm += 500;
 else if (rpm >= 1000)
 rpm += 100;
 else if (rpm >= 500)
 rpm += 50;
 else if (rpm >= 100)
 rpm += 10;
 else if (rpm >= 50)
 rpm += 5;
 else
 rpm += 1;
 }
 if (delta < 0 && rpm > 0)
 {
// EDITED BEGIN
/*
 if (encPos == 0)
 rpm = 0;
 else
*/
// EDITED END

  if (rpm <= 10)
 rpm -= 1;
 else if (rpm <= 50)
 rpm -= 1;
 else if (rpm <= 100)
 rpm -= 5;
 else if (rpm <= 500)
 rpm -= 10;
 else if (rpm <= 1000)
 rpm -= 50;
 else if (rpm <= 5000)
 rpm -= 100;
 else if (rpm <= 10000)
 rpm -= 500;
 else if (rpm <= 50000)
 rpm -= 1000;
 else if (rpm <= 100000)
 rpm -= 5000;
 else
 rpm -= 10000;
 }

 if (rpm != rpmOld)
 {
 if (rpm != 0)
 {
 float period = 1000000.0 / (rpm / 60.0); // micros
 float Hz = 1.0 / ((float)period / 1000000.0);

 Timer1.setPeriod(period); // micros
 Timer1.pwm(OUT_PIN, 100);

#ifdef __SERIAL_DEBUG
 Serial.print(encPos); Serial.print(" ");
 Serial.print("RPM = "); Serial.print(rpm);
 Serial.print(" / Period = "); Serial.print((float)period/1000.0);
 Serial.print(" ms / Freq. = "); Serial.print(Hz);
 Serial.println(" Hz");
#endif
 char line0[64] = "";
 String strHz(Hz);
 sprintf(line0, "%lu RPM %s Hz", rpm, strHz.c_str());

 char line1[64] = "";
 String strPeriod((float)period/1000);
 sprintf(line1, "%s ms", strPeriod.c_str());

 u8g2_uint_t x0 = (128 - oled.getStrWidth(line0)) >> 1;
 u8g2_uint_t x1 = (128 - oled.getStrWidth(line1)) >> 1;

 oled.clearBuffer();
 oled.drawStr(x0, 10, line0);
 oled.drawStr(x1, 30, line1);
 oled.sendBuffer();
 }
 else
 {
 digitalWrite(OUT_PIN, LOW);

#ifdef __SERIAL_DEBUG
 Serial.print("RPM = "); Serial.print(rpm);
 Serial.print(" Period = "); Serial.print("N/A");
 Serial.print(" Freq. = "); Serial.println(0);
#endif
 char line0[64] = "";
 sprintf(line0, "%lu RPM 0.00 Hz", rpm);

 oled.clearBuffer();
 oled.setFont(u8g2_font_6x10_tf);
 oled.drawStr((128 - oled.getStrWidth(line0)) >> 1, 10, line0);
 oled.sendBuffer();
 }

 rpmOld = rpm;
 }
}

Attachment = working electronics ; with a USB cable, it reboots on and on...

Post a photo of the pcb view of the 128x32.

Does it contain a 3.3V regulator?
Probably printed U2

You know what ? You solved the problem !

This OLED was advertised as 5V/3.3V (and it worked fine with 5V Vcc / 5V tolerant STM32 standard I²C pins). It has pullups (AFAIK...).

And yes, it has a 3.3V regulator.

So, I gave it 3.3V on Vcc, and used MOSFETs shifters for SDA/SCL : works fine with USB. I also gave it 3.3V on Vcc, without shifters : works fine RANDOMLY.

It does not like 5Vcc. 3.3V, and problem solved ! (but I don't understand why)

Attached the OLED PCB photo.

Slighly edited the source in the OP (minor bug) : the edited lines are now surrounded with "EDITED"

Maybe it could be usefull to someone : it generates a signal that emulates a tachometer probe, with one pulse per rev, from 0 to 500000 RPM, TTL, positive pulses, 10% duty cycle, the encoder works with a pseudo logarithmic scale. Display : RPM, frequency (Hz), period (milliseconds) ; push button -> 25Hz/1500RPM

[EDIT] seems to work reliably with just 3V3 on Vcc, without shifters, if there is no USB hub between the PC and the Arduino

DSC_7489.JPG

Yes, Q1 looks like a regulator (662K)
The convention is to use U# for an i.c. and Q# for a transistor.

I would expect it to work with 5V on VCC pin. (Or even 3.3V on VCC pin)
If there was no regulator, you must connect 3.3V to VCC pin.

You will not need level shifters. The SSD1306 seems to be input voltage forgiving rather than tolerant.

David.

I tested with 2 other OLEDs : makes no difference.

I swapped the Nano with another I had to take apart from another project, and no problems with USB, even with 2 cascaded USB hubs.

When on USB, it enters a bootloop, appearing and disappearing from the device manager. Sometimes, after 10 to 20 reboots, it works, and the firmware runs normally ; but when it runs, it does not show in the device manager. Occurs only if the I²C OLED is connected. I will also test with a I²C LCD.

This Nano or its CH340 probably have an issue. I will also burn the bootloader again, who knows ?

This Nano is definitely the issue. Bootloader was re-burnt (using a Pro-Mini, with no problem), but the problem is still there. It's old, and has probably been somewhat abused ! (I generally develop projects on Nanos, before using a Pro Mini in the project). It's the second one I kill.

Surprisingly, Nanos are more expensive than STM32s... 8bit vs 32, 16MHz vs 72MHz (with no OC...), but they are so much easier to use !