🐛 Fix STM32 I2C 2-wire LCD, Soft I2C impl. (#26433)
Some checks are pending
CI - Build Tests / Build Test (push) Waiting to run
CI - Unit Tests / Unit Test (push) Waiting to run
CI - Validate Source Files / Validate Source Files (push) Waiting to run

This commit is contained in:
Bob Kuhn 2025-09-27 02:25:03 -05:00 committed by GitHub
parent 5af8425776
commit 6e74409c14
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 264 additions and 19 deletions

View file

@ -30,3 +30,6 @@ uint8_t u8g_com_HAL_STM32_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, vo
uint8_t u8g_com_stm32duino_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); // See U8glib-HAL
#define U8G_COM_HAL_HW_SPI_FN u8g_com_stm32duino_hw_spi_fn
uint8_t u8g_com_stm32duino_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); // u8g_com_stm32duino_ssd_i2c.cpp
#define U8G_COM_SSD_I2C_HAL u8g_com_stm32duino_ssd_i2c_fn

View file

@ -0,0 +1,194 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/**
* 2-Wire I2C COM Driver
*
* Handles both Hardware and Software I2C so any pins can be used as SDA and SLC.
* Wire library is used for Hardware I2C.
* SlowSoftWire is used for Software I2C.
*
* Wire / SoftWire library selection can be done automatically at runtime.
*
* SDA and SLC pins must be named DOGLCD_SDA_PIN, DOGLCD_SCL_PIN to distinguish
* from other I2C devices (e.g., EEPROM) that use I2C_SDA_PIN, I2C_SLC_PIN.
*/
#ifdef ARDUINO_ARCH_STM32
#include "../../../inc/MarlinConfig.h"
#if HAS_U8GLIB_I2C_OLED
#include <U8glib-HAL.h>
#if ENABLED(U8G_USES_HW_I2C)
#include <Wire.h>
#ifndef MASTER_ADDRESS
#define MASTER_ADDRESS 0x01
#endif
#endif
#if ENABLED(U8G_USES_SW_I2C)
#include <SlowSoftI2CMaster.h>
#include <SlowSoftWire.h>
#endif
/**
* BUFFER_LENGTH is defined in libraries\Wire\utility\WireBase.h
* Default value is 32
* Increase this value to 144 to send U8G_COM_MSG_WRITE_SEQ in single block
*/
#ifndef BUFFER_LENGTH
#define BUFFER_LENGTH 32
#endif
#if BUFFER_LENGTH > 144
#error "BUFFER_LENGTH should not be greater than 144."
#endif
#define I2C_MAX_LENGTH (BUFFER_LENGTH - 1)
uint8_t u8g_com_stm32duino_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) {
// Hardware I2C flag
#ifdef COMPILE_TIME_I2C_IS_HARDWARE
constexpr bool isHardI2C = ENABLED(COMPILE_TIME_I2C_IS_HARDWARE);
#else
static bool isHardI2C = false;
static bool i2c_initialized = false; // Flag to only run init/linking code once
if (!i2c_initialized) { // Init runtime linkages
i2c_initialized = true; // Only do this once
I2C_TypeDef *i2cInstance1 = (I2C_TypeDef *)pinmap_peripheral(digitalPinToPinName(DOGLCD_SDA_PIN), PinMap_I2C_SDA);
I2C_TypeDef *i2cInstance2 = (I2C_TypeDef *)pinmap_peripheral(digitalPinToPinName(DOGLCD_SCL_PIN), PinMap_I2C_SCL);
isHardI2C = (i2cInstance1 && (i2cInstance1 == i2cInstance2)); // Found hardware I2C controller
}
#endif
static uint8_t msgInitCount = 0; // Ignore all messages until 2nd U8G_COM_MSG_INIT
if (msgInitCount) {
if (msg == U8G_COM_MSG_INIT) msgInitCount--;
if (msgInitCount) return -1;
}
static uint8_t control;
if (isHardI2C) { // Found hardware I2C controller
#if ENABLED(U8G_USES_HW_I2C)
static TwoWire wire2; // A TwoWire object for use below
switch (msg) {
case U8G_COM_MSG_INIT:
wire2.setClock(400000);
wire2.setSCL(DOGLCD_SCL_PIN);
wire2.setSDA(DOGLCD_SDA_PIN);
wire2.begin(MASTER_ADDRESS, 0); // Start as master
break;
case U8G_COM_MSG_ADDRESS: // Define cmd (arg_val = 0) or data mode (arg_val = 1)
control = arg_val ? 0x40 : 0x00;
break;
case U8G_COM_MSG_WRITE_BYTE:
wire2.beginTransmission(0x3C);
wire2.write(control);
wire2.write(arg_val);
wire2.endTransmission();
break;
case U8G_COM_MSG_WRITE_SEQ: {
uint8_t* dataptr = (uint8_t*)arg_ptr;
#ifdef I2C_MAX_LENGTH
while (arg_val > 0) {
wire2.beginTransmission(0x3C);
wire2.write(control);
if (arg_val <= I2C_MAX_LENGTH) {
wire2.write(dataptr, arg_val);
arg_val = 0;
}
else {
wire2.write(dataptr, I2C_MAX_LENGTH);
arg_val -= I2C_MAX_LENGTH;
dataptr += I2C_MAX_LENGTH;
}
wire2.endTransmission();
}
#else
wire2.beginTransmission(0x3C);
wire2.write(control);
wire2.write(dataptr, arg_val);
wire2.endTransmission();
#endif // I2C_MAX_LENGTH
break;
}
}
#endif // U8G_USES_HW_I2C
}
else { // Software I2C
#if ENABLED(U8G_USES_SW_I2C)
static SlowSoftWire sWire = SlowSoftWire(DOGLCD_SDA_PIN, DOGLCD_SCL_PIN);
switch (msg) {
case U8G_COM_MSG_INIT:
sWire.setClock(400000);
sWire.begin(); // Start as master
break;
case U8G_COM_MSG_ADDRESS: // Define cmd (arg_val = 0) or data mode (arg_val = 1)
control = arg_val ? 0x40 : 0x00;
break;
case U8G_COM_MSG_WRITE_BYTE:
sWire.beginTransmission((uint8_t)0x3C);
sWire.write((uint8_t)control);
sWire.write((uint8_t)arg_val);
sWire.endTransmission();
break;
case U8G_COM_MSG_WRITE_SEQ: {
uint8_t* dataptr = (uint8_t*)arg_ptr;
#ifdef I2C_MAX_LENGTH
while (arg_val > 0) {
sWire.beginTransmission((uint8_t)0x3C);
sWire.write((uint8_t)control);
if (arg_val <= I2C_MAX_LENGTH) {
sWire.write((const uint8_t *)dataptr, (size_t)arg_val);
arg_val = 0;
}
else {
sWire.write((const uint8_t *)dataptr, I2C_MAX_LENGTH);
arg_val -= I2C_MAX_LENGTH;
dataptr += I2C_MAX_LENGTH;
}
sWire.endTransmission();
}
#else
sWire.beginTransmission((uint8_t)0x3C);
sWire.write((uint8_t)control);
sWire.write((const uint8_t *)dataptr, (size_t)arg_val);
sWire.endTransmission();
#endif // I2C_MAX_LENGTH
break;
}
}
#endif // U8G_USES_SW_I2C
}
return 1;
}
#endif // HAS_U8GLIB_I2C_OLED
#endif // ARDUINO_ARCH_STM32

View file

@ -354,7 +354,22 @@
// ...and 128x64 SPI OLED LCDs (SSD1306 / SH1106)
#if ANY(U8GLIB_SSD1306, U8GLIB_SSD1309, U8GLIB_SH1106)
#define HAS_U8GLIB_I2C_OLED 1
// Define this to reduce build size and optimize performance
//#define COMPILE_TIME_I2C_IS_HARDWARE true // true: Hardware false: Software undefined: Solve at runtime
#ifdef COMPILE_TIME_I2C_IS_HARDWARE
#if COMPILE_TIME_I2C_IS_HARDWARE
#define U8G_USES_HW_I2C
#else
#define U8G_USES_SW_I2C
#endif
#else
#define U8G_USES_HW_I2C
#define U8G_USES_SW_I2C
#endif
#endif
#if ANY(HAS_U8GLIB_I2C_OLED, U8GLIB_SSD1306_SPI, U8GLIB_SH1106_SPI)
#define HAS_WIRED_LCD 1
#define DOGLCD

View file

@ -310,7 +310,22 @@ void MarlinUI::init_lcd() {
#endif
#if ANY(MKS_12864OLED, MKS_12864OLED_SSD1306, FYSETC_242_OLED_12864, ZONESTAR_12864OLED, K3D_242_OLED_CONTROLLER)
SET_OUTPUT(LCD_PINS_DC);
#if defined(LCD_PINS_DC) && LCD_PINS_DC != -1
#if IS_I2C_LCD
I2C_TypeDef *i2cInstance1 = (I2C_TypeDef *)pinmap_peripheral(digitalPinToPinName(DOGLCD_SDA_PIN), PinMap_I2C_SDA);
I2C_TypeDef *i2cInstance2 = (I2C_TypeDef *)pinmap_peripheral(digitalPinToPinName(DOGLCD_SCL_PIN), PinMap_I2C_SCL);
const bool isSoftI2C = !(i2cInstance1 && (i2cInstance1 == i2cInstance2)); // Using software I2C driver for LCD
#else
constexpr bool isSoftI2C = false;
#endif
if (!isSoftI2C) SET_OUTPUT(LCD_PINS_DC); // For these LCDs, set as output if not using software I2C driver
#endif
#ifndef LCD_RESET_PIN
#define LCD_RESET_PIN LCD_PINS_RS
#endif
#endif
#if PIN_EXISTS(LCD_RESET)

View file

@ -32,6 +32,11 @@
//#define ALTERNATIVE_LCD
// Defined DOGLCD_SDA_PIN and DOGLCD_SCL_PIN pins indicate I2C LCD
#if PINS_EXIST(DOGLCD_SDA, DOGLCD_SCL)
#define IS_I2C_LCD 1
#endif
#if ENABLED(REPRAPWORLD_GRAPHICAL_LCD)
// RepRapWorld Graphical LCD
@ -126,12 +131,15 @@
// MKS 128x64 (SSD1306) OLED I2C LCD
#define FORCE_SOFT_SPI // SW-SPI
#if ENABLED(ALTERNATIVE_LCD)
#define U8G_CLASS U8GLIB_SSD1306_128X64_2X // 4 stripes
#if IS_I2C_LCD
#define U8G_CLASS U8GLIB_SSD1306_128X64_2X_I2C_2_WIRE // I2C
#else
#define U8G_CLASS U8GLIB_SSD1306_128X64 // 8 stripes
#define FORCE_SOFT_SPI // SW-SPI
#if ENABLED(ALTERNATIVE_LCD)
#define U8G_CLASS U8GLIB_SSD1306_128X64_2X // 4 stripes
#else
#define U8G_CLASS U8GLIB_SSD1306_128X64 // 8 stripes
#endif
#endif
#elif ANY(FYSETC_242_OLED_12864, K3D_242_OLED_CONTROLLER)
@ -168,7 +176,9 @@
// - or -
// Zonestar SH1106 OLED SPI LCD
#define FORCE_SOFT_SPI // SW-SPI
#if !IS_I2C_LCD
#define FORCE_SOFT_SPI // SW-SPI
#endif
#if ENABLED(ALTERNATIVE_LCD)
#define U8G_CLASS U8GLIB_SH1106_128X64_2X // 4 stripes
#else
@ -238,7 +248,9 @@
// Use HW-SPI if no other option is specified
#ifndef U8G_PARAM
#if ENABLED(FORCE_SOFT_SPI)
#if IS_I2C_LCD
#define U8G_PARAM U8G_I2C_OPT_NONE // I2C LCD
#elif ENABLED(FORCE_SOFT_SPI)
#define U8G_PARAM DOGLCD_SCK, DOGLCD_MOSI, DOGLCD_CS, DOGLCD_A0 // SW-SPI
#else
#define U8G_PARAM DOGLCD_CS, DOGLCD_A0 // HW-SPI

View file

@ -78,7 +78,7 @@ class U8GLIB_SH1106_128X64_2X_I2C_2_WIRE : public U8GLIB {
public:
U8GLIB_SH1106_128X64_2X_I2C_2_WIRE() : U8GLIB() { }
U8GLIB_SH1106_128X64_2X_I2C_2_WIRE(uint8_t options) { init(options); }
void init(uint8_t options = U8G_I2C_OPT_NONE) { U8GLIB::init(&u8g_dev_sh1106_128x64_2x_i2c_2_wire, options); }
void init(uint8_t options=U8G_I2C_OPT_NONE) { U8GLIB::init(&u8g_dev_sh1106_128x64_2x_i2c_2_wire, options); }
};
extern u8g_dev_t u8g_dev_ssd1306_128x64_2x_i2c_2_wire;
@ -87,7 +87,7 @@ class U8GLIB_SSD1306_128X64_2X_I2C_2_WIRE : public U8GLIB {
public:
U8GLIB_SSD1306_128X64_2X_I2C_2_WIRE() : U8GLIB() { }
U8GLIB_SSD1306_128X64_2X_I2C_2_WIRE(uint8_t options) { init(options); }
void init(uint8_t options = U8G_I2C_OPT_NONE) { U8GLIB::init(&u8g_dev_ssd1306_128x64_2x_i2c_2_wire, options); }
void init(uint8_t options=U8G_I2C_OPT_NONE) { U8GLIB::init(&u8g_dev_ssd1306_128x64_2x_i2c_2_wire, options); }
};
#if ENABLED(U8GLIB_SH1106_SPI)

View file

@ -67,7 +67,7 @@
#include "../../../inc/MarlinConfigPre.h"
#if HAS_MARLINUI_U8GLIB
#if HAS_U8GLIB_I2C_OLED
#include "HAL_LCD_com_defines.h"
@ -97,6 +97,7 @@
#define CMD_NOOP() (0xE3)
uint8_t u8g_WriteEscSeqP_2_wire(u8g_t *u8g, u8g_dev_t *dev, const uint8_t *esc_seq);
uint8_t u8g_Write_Init_Sequence_2_wire(u8g_t *u8g, u8g_dev_t *dev, uint32_t length, const uint8_t *init_seq);
// SH1106 is compatible with SSD1306, but is 132x64. Display 128x64 centered within the 132x64.
@ -133,7 +134,7 @@ uint8_t u8g_dev_sh1106_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t m
switch (msg) {
case U8G_DEV_MSG_INIT:
u8g_InitCom(u8g, dev, U8G_SPI_CLK_CYCLE_300NS);
u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_sh1106_128x64_init_seq_2_wire);
u8g_Write_Init_Sequence_2_wire(u8g, dev, COUNT(u8g_dev_sh1106_128x64_init_seq_2_wire), u8g_dev_sh1106_128x64_init_seq_2_wire);
break;
case U8G_DEV_MSG_STOP: break;
case U8G_DEV_MSG_PAGE_NEXT: {
@ -151,7 +152,7 @@ uint8_t u8g_dev_sh1106_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t m
u8g_WriteSequence(u8g, dev, pb->width, (uint8_t *)(pb->buf)+pb->width);
u8g_SetChipSelect(u8g, dev, 0);
} break;
case U8G_DEV_MSG_SLEEP_ON: return 1;
case U8G_DEV_MSG_SLEEP_ON:
case U8G_DEV_MSG_SLEEP_OFF: return 1;
}
return u8g_dev_pb16v1_base_fn(u8g, dev, msg, arg);
@ -169,7 +170,6 @@ static const uint8_t u8g_dev_ssd1306_128x64_data_start_2_wire[] PROGMEM = {
};
static const uint8_t u8g_dev_ssd1306_128x64_init_seq_2_wire[] PROGMEM = {
U8G_ESC_CS(0), // Disable chip
CMD_ON(0), // Display OFF, sleep mode
CMD_MUX_RATIO(0x3F), // Mux ratio
CMD_DISP_OFFS(0), // Display offset
@ -196,7 +196,7 @@ uint8_t u8g_dev_ssd1306_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t
switch (msg) {
case U8G_DEV_MSG_INIT:
u8g_InitCom(u8g, dev, U8G_SPI_CLK_CYCLE_300NS);
u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_ssd1306_128x64_init_seq_2_wire);
u8g_Write_Init_Sequence_2_wire(u8g, dev, COUNT(u8g_dev_ssd1306_128x64_init_seq_2_wire), u8g_dev_ssd1306_128x64_init_seq_2_wire);
break;
case U8G_DEV_MSG_STOP: break;
case U8G_DEV_MSG_PAGE_NEXT: {
@ -214,7 +214,7 @@ uint8_t u8g_dev_ssd1306_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t
u8g_WriteSequence(u8g, dev, pb->width, (uint8_t *)(pb->buf)+pb->width);
u8g_SetChipSelect(u8g, dev, 0);
} break;
case U8G_DEV_MSG_SLEEP_ON: return 1;
case U8G_DEV_MSG_SLEEP_ON:
case U8G_DEV_MSG_SLEEP_OFF: return 1;
}
return u8g_dev_pb16v1_base_fn(u8g, dev, msg, arg);
@ -283,4 +283,10 @@ uint8_t u8g_WriteEscSeqP_2_wire(u8g_t *u8g, u8g_dev_t *dev, const uint8_t *esc_s
return 1;
}
#endif // HAS_MARLINUI_U8GLIB
uint8_t u8g_Write_Init_Sequence_2_wire(u8g_t *u8g, u8g_dev_t *dev, uint32_t length, const uint8_t *init_seq) {
u8g_SetAddress(u8g, dev, 0); // Instruction mode
u8g_WriteSequence(u8g, dev, length, (uint8_t*)init_seq);
return 1;
}
#endif // HAS_U8GLIB_I2C_OLED

View file

@ -13,5 +13,5 @@ restore_configs
opt_set MOTHERBOARD BOARD_BTT_SKR_E3_DIP \
SERIAL_PORT 1 SERIAL_PORT_2 -1 \
X_DRIVER_TYPE TMC2209 Y_DRIVER_TYPE TMC2130
opt_enable SERIAL_DMA
opt_enable SERIAL_DMA ULTI_CONTROLLER
exec_test $1 $2 "BTT SKR E3 DIP 1.0 | Mixed TMC Drivers" "$3"

View file

@ -43,7 +43,7 @@ HAS_MARLINUI_U8GLIB = marlinfirmware/U8glib-HAL@0.5.5
build_src_filter=+<src/lcd/dogm>
HAS_(FSMC|SPI|LTDC)_TFT = build_src_filter=+<src/lcd/tft_io>
I2C_EEPROM = build_src_filter=+<src/HAL/shared/eeprom_if_i2c.cpp>
SOFT_I2C_EEPROM = SlowSoftI2CMaster, SlowSoftWire=https://github.com/felias-fogg/SlowSoftWire/archive/f34d777f39.zip
SOFT_I2C_EEPROM|U8G_USES_SW_I2C = SlowSoftI2CMaster, SlowSoftWire=https://github.com/felias-fogg/SlowSoftWire/archive/f34d777f39.zip
SPI_EEPROM = build_src_filter=+<src/HAL/shared/eeprom_if_spi.cpp>
HAS_DWIN_E3V2|IS_DWIN_MARLINUI = build_src_filter=+<src/lcd/e3v2/common>
DWIN_CREALITY_LCD = build_src_filter=+<src/lcd/e3v2/creality>