From f5df78c1ab207b98638afdd965f0f015e9531c18 Mon Sep 17 00:00:00 2001 From: ProfessorKa0Z Date: Mon, 9 Mar 2026 19:16:59 -0700 Subject: [PATCH 1/2] Flipping orientation of OLEDs --- .gitignore | 1 - Display.h | 6 ++++ Display_Implementation.h | 21 ++++++++++---- IO_HALDisplay.h | 12 ++++---- LiquidCrystal_I2C.cpp | 2 +- LiquidCrystal_I2C.h | 3 +- SSD1306Ascii.cpp | 61 ++++++++++++++++++++++++++++++++++++---- SSD1306Ascii.h | 10 +++++-- config.example.h | 2 ++ myHal.cpp_example.txt | 4 +++ 10 files changed, 99 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index b7f8278f1..0f3b4412a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ mySetup.cpp myHal.cpp myFilter.cpp my*.h -!my*.example.h compile_commands.json newcode.txt.old UserAddin.txt diff --git a/Display.h b/Display.h index 467424f7d..af07f13af 100644 --- a/Display.h +++ b/Display.h @@ -22,6 +22,12 @@ #include "defines.h" #include "DisplayInterface.h" +// Used for OLED displays. Orientation is not implemented for LCDs, but they share "HALDisplay::create" constructors +enum class Orientation { + normal, + flipped +}; + // Allow maximum message length to be overridden from config.h #if !defined(MAX_MSG_SIZE) #define MAX_MSG_SIZE 20 diff --git a/Display_Implementation.h b/Display_Implementation.h index 6a3c99531..936d73942 100644 --- a/Display_Implementation.h +++ b/Display_Implementation.h @@ -40,12 +40,21 @@ // LiquidCrystal_I2C for I2C LCD driver for HD44780 with PCF8574 'backpack'. #if defined(OLED_DRIVER) - #define DISPLAY_START(xxx) { \ - DisplayInterface *t = new Display(new SSD1306AsciiWire(OLED_DRIVER)); \ - t->begin(); \ - xxx; \ - t->refresh(); \ - } + #if defined(FLIP_OLED) + #define DISPLAY_START(xxx) { \ + DisplayInterface *t = new Display(new SSD1306AsciiWire(OLED_DRIVER, Orientation::flipped)); \ + t->begin(); \ + xxx; \ + t->refresh(); \ + } + #else + #define DISPLAY_START(xxx) { \ + DisplayInterface *t = new Display(new SSD1306AsciiWire(OLED_DRIVER)); \ + t->begin(); \ + xxx; \ + t->refresh(); \ + } + #endif #elif defined(LCD_DRIVER) #define DISPLAY_START(xxx) { \ diff --git a/IO_HALDisplay.h b/IO_HALDisplay.h index 5bf16a119..1852c9ae6 100644 --- a/IO_HALDisplay.h +++ b/IO_HALDisplay.h @@ -83,17 +83,17 @@ class HALDisplay : public IODevice, public DisplayInterface { public: // Static function to handle "HALDisplay::create(...)" calls. - static void create(I2CAddress i2cAddress, int width, int height) { - if (checkNoOverlap(0, 0, i2cAddress)) new HALDisplay(0, i2cAddress, width, height); + static void create(I2CAddress i2cAddress, int width, int height, Orientation orientation = Orientation::normal) { + if (checkNoOverlap(0, 0, i2cAddress)) new HALDisplay(0, i2cAddress, width, height, orientation); } - static void create(uint8_t displayNo, I2CAddress i2cAddress, int width, int height) { - if (checkNoOverlap(0, 0, i2cAddress)) new HALDisplay(displayNo, i2cAddress, width, height); + static void create(uint8_t displayNo, I2CAddress i2cAddress, int width, int height, Orientation orientation = Orientation::normal) { + if (checkNoOverlap(0, 0, i2cAddress)) new HALDisplay(displayNo, i2cAddress, width, height, orientation); } protected: // Constructor - HALDisplay(uint8_t displayNo, I2CAddress i2cAddress, int width, int height) { - _displayDriver = new T(i2cAddress, width, height); + HALDisplay(uint8_t displayNo, I2CAddress i2cAddress, int width, int height, Orientation orientation) { + _displayDriver = new T(i2cAddress, width, height, orientation); if (!_displayDriver) return; // Check for memory allocation failure _I2CAddress = i2cAddress; _width = width; diff --git a/LiquidCrystal_I2C.cpp b/LiquidCrystal_I2C.cpp index a517f6145..5ee328fec 100644 --- a/LiquidCrystal_I2C.cpp +++ b/LiquidCrystal_I2C.cpp @@ -42,7 +42,7 @@ // LiquidCrystal constructor is called). LiquidCrystal_I2C::LiquidCrystal_I2C(I2CAddress lcd_Addr, uint8_t lcd_cols, - uint8_t lcd_rows) { + uint8_t lcd_rows, Orientation orientation) { _Addr = lcd_Addr; lcdRows = lcd_rows; // Number of character rows (typically 2 or 4). lcdCols = lcd_cols; // Number of character columns (typically 16 or 20) diff --git a/LiquidCrystal_I2C.h b/LiquidCrystal_I2C.h index 650ad151a..33ccf4a2e 100644 --- a/LiquidCrystal_I2C.h +++ b/LiquidCrystal_I2C.h @@ -64,7 +64,8 @@ class LiquidCrystal_I2C : public DisplayDevice { public: - LiquidCrystal_I2C(I2CAddress lcd_Addr,uint8_t lcd_cols,uint8_t lcd_rows); + // Changing orientation not implemented for LCDs, but provide default parameter for compatibility with other display drivers + LiquidCrystal_I2C(I2CAddress lcd_Addr,uint8_t lcd_cols,uint8_t lcd_rows, Orientation orientation = Orientation::normal); bool begin() override; void clearNative() override; void setRowNative(byte line) override; diff --git a/SSD1306Ascii.cpp b/SSD1306Ascii.cpp index ea0c76b37..046db43dd 100644 --- a/SSD1306Ascii.cpp +++ b/SSD1306Ascii.cpp @@ -115,6 +115,27 @@ const uint8_t FLASH SSD1306AsciiWire::Adafruit128xXXinit[] = { SSD1306_DISPLAYON }; +const uint8_t FLASH SSD1306AsciiWire::Adafruit128xXXinit_FLIP[] = { + // Init sequence changing SEGREMAP and COMSCAN to flip display orientation + 0x00, // Set to command mode + SSD1306_DISPLAYOFF, + SSD1306_SETDISPLAYCLOCKDIV, 0x80, // the suggested ratio 0x80 + SSD1306_SETMULTIPLEX, 0x3F, // ratio 64 (initially) + SSD1306_SETDISPLAYOFFSET, 0x0, // no offset + SSD1306_SETSTARTLINE | 0x0, // line #0 + SSD1306_CHARGEPUMP, 0x14, // internal vcc + SSD1306_MEMORYMODE, 0x02, // page mode + SSD1306_SEGREMAP | 0x0, // column 0 mapped to SEG0 + SSD1306_COMSCANINC, // column scan direction normal + SSD1306_SETCOMPINS, 0X12, // set COM pins + SSD1306_SETCONTRAST, 0x7F, // contrast level 127 + SSD1306_SETPRECHARGE, 0xF1, // pre-charge period (1, 15) + SSD1306_SETVCOMDETECT, 0x40, // vcomh regulator level + SSD1306_DISPLAYALLON_RESUME, + SSD1306_NORMALDISPLAY, + SSD1306_DISPLAYON +}; + //------------------------------------------------------------------------------ // This section is based on https://github.com/stanleyhuangyc/MultiLCD @@ -139,19 +160,41 @@ const uint8_t FLASH SSD1306AsciiWire::SH1106_132x64init[] = { SSD1306_DISPLAYON }; +// Init sequence changing SEGREMAP and COMSCAN to flip display orientation +const uint8_t FLASH SSD1306AsciiWire::SH1106_132x64init_FLIP[] = { + 0x00, // Set to command mode + SSD1306_DISPLAYOFF, + SSD1306_SETDISPLAYCLOCKDIV, 0X80, // set osc division + SSD1306_SETMULTIPLEX, 0x3F, // ratio 64 + SSD1306_SETDISPLAYOFFSET, 0X00, // set display offset + SSD1306_SETSTARTPAGE | 0X0, // set page address + SSD1306_SETSTARTLINE | 0x0, // set start line + SH1106_SET_PUMP_MODE, SH1106_PUMP_ON, // set charge pump enable + SSD1306_SEGREMAP | 0x0, // column 0 mapped to SEG0 + SSD1306_COMSCANINC, // column scan direction normal + SSD1306_SETCOMPINS, 0X12, // set COM pins + SSD1306_SETCONTRAST, 0x80, // 128 + SSD1306_SETPRECHARGE, 0X1F, // set pre-charge period + SSD1306_SETVCOMDETECT, 0x40, // set vcomh + SH1106_SET_PUMP_VOLTAGE | 0X2, // 8.0 volts + SSD1306_NORMALDISPLAY, // normal / reverse + SSD1306_DISPLAYON +}; + //============================================================================== // SSD1306AsciiWire Method Definitions //------------------------------------------------------------------------------ // Auto-detect address -SSD1306AsciiWire::SSD1306AsciiWire(int width, int height) - : SSD1306AsciiWire(0, width, height) { } +SSD1306AsciiWire::SSD1306AsciiWire(int width, int height, Orientation orientation) + : SSD1306AsciiWire(0, width, height, orientation) { } // Constructor with explicit address -SSD1306AsciiWire::SSD1306AsciiWire(I2CAddress address, int width, int height) { +SSD1306AsciiWire::SSD1306AsciiWire(I2CAddress address, int width, int height, Orientation orientation) { m_i2cAddr = address; m_displayWidth = width; m_displayHeight = height; + m_orientation = orientation; // Set size in characters m_charsPerColumn = m_displayHeight / fontHeight; m_charsPerRow = (m_displayWidth+fontWidth-1) / fontWidth; // Round up @@ -180,10 +223,18 @@ bool SSD1306AsciiWire::begin() { if (m_displayWidth==132 && m_displayHeight==64) { // SH1106 display. This uses 128x64 centered within a 132x64 OLED. m_colOffset = 2; - I2CManager.write_P(m_i2cAddr, SH1106_132x64init, sizeof(SH1106_132x64init)); + if (m_orientation == Orientation::flipped) { + I2CManager.write_P(m_i2cAddr, SH1106_132x64init_FLIP, sizeof(SH1106_132x64init_FLIP)); + } else { + I2CManager.write_P(m_i2cAddr, SH1106_132x64init, sizeof(SH1106_132x64init)); + } } else if (m_displayWidth==128 && (m_displayHeight==64 || m_displayHeight==32)) { // SSD1306 or SSD1309 128x64 or 128x32 - I2CManager.write_P(m_i2cAddr, Adafruit128xXXinit, sizeof(Adafruit128xXXinit)); + if (m_orientation == Orientation::flipped) { + I2CManager.write_P(m_i2cAddr, Adafruit128xXXinit_FLIP, sizeof(Adafruit128xXXinit_FLIP)); + } else { + I2CManager.write_P(m_i2cAddr, Adafruit128xXXinit, sizeof(Adafruit128xXXinit)); + } if (m_displayHeight == 32) I2CManager.write(m_i2cAddr, 5, 0, // Set command mode SSD1306_SETMULTIPLEX, 0x1F, // ratio 32 diff --git a/SSD1306Ascii.h b/SSD1306Ascii.h index 57427a1ad..59e656a4b 100644 --- a/SSD1306Ascii.h +++ b/SSD1306Ascii.h @@ -39,9 +39,9 @@ class SSD1306AsciiWire : public DisplayDevice { public: - // Constructors - SSD1306AsciiWire(int width, int height); // Auto-detects I2C address - SSD1306AsciiWire(I2CAddress address, int width, int height); + // Constructors; optional orientation paramater defaults to normal + SSD1306AsciiWire(int width, int height, Orientation orientation = Orientation::normal); // Auto-detects I2C address + SSD1306AsciiWire(I2CAddress address, int width, int height, Orientation orientation = Orientation::normal); // Initialize the display controller. bool begin(); @@ -68,6 +68,8 @@ class SSD1306AsciiWire : public DisplayDevice { uint8_t m_displayWidth; // Display height. uint8_t m_displayHeight; + // Rotation of display + Orientation m_orientation; // Display width in characters uint8_t m_charsPerRow; // Display height in characters @@ -95,6 +97,8 @@ class SSD1306AsciiWire : public DisplayDevice { static const uint8_t System6x8[]; static const uint8_t FLASH Adafruit128xXXinit[]; static const uint8_t FLASH SH1106_132x64init[]; + static const uint8_t FLASH Adafruit128xXXinit_FLIP[]; + static const uint8_t FLASH SH1106_132x64init_FLIP[]; }; #endif // SSD1306Ascii_h diff --git a/config.example.h b/config.example.h index b505c8718..38463517a 100644 --- a/config.example.h +++ b/config.example.h @@ -170,6 +170,8 @@ The configuration file for DCC-EX Command Station // 128x32 or 128x64 I2C SSD1306-based devices are supported. // Use 132,64 for a SH1106-based I2C device with a 128x64 display. // #define OLED_DRIVER 0x3c,128,32 +// Uncomment the following line to flip the vertical orientation of the OLED display +// #define FLIP_OLED // Define scroll mode as 0, 1 or 2 // * #define SCROLLMODE 0 is scroll continuous (fill screen if poss), diff --git a/myHal.cpp_example.txt b/myHal.cpp_example.txt index 144fe1a09..809d5ca9d 100644 --- a/myHal.cpp_example.txt +++ b/myHal.cpp_example.txt @@ -50,6 +50,10 @@ void halSetup() { //HALDisplay::create(1, 0x3d, 128, 32); + // To invert the orientation of the OLED, add the Orientation::flipped parameter: + + // HALDisplay::create(1, 0x3d, 128, 32, Orientation::flipped); + // Create a 20x4 LCD display device as display number 2 // (line 0 is written by EX-RAIL 'SCREEN(2, 0, "text")'). From c2f268a23097a614b644f546f188314f12991c16 Mon Sep 17 00:00:00 2001 From: ProfessorKa0Z <145606954+ProfessorKa0Z@users.noreply.github.com> Date: Tue, 10 Mar 2026 20:42:30 -0700 Subject: [PATCH 2/2] Restore .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0f3b4412a..b7f8278f1 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ mySetup.cpp myHal.cpp myFilter.cpp my*.h +!my*.example.h compile_commands.json newcode.txt.old UserAddin.txt