Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

STM32 I2C two wire LCD - bug fix and soft I2C driver implementation #26433

Open
wants to merge 36 commits into
base: bugfix-2.1.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
637983d
F103 testing
Bob-the-Kuhn Nov 15, 2023
14e57de
Update u8g_com_stm32duino_ssd_i2c.cpp
Bob-the-Kuhn Nov 16, 2023
e581b2b
Added protection to keep from un-necessarily compiling u8g_dev_ssd130…
Bob-the-Kuhn Nov 17, 2023
e7f6fdc
misc. cleanup
thinkyhead Nov 25, 2023
2c3f6cd
run time selection of soft vs. hard I2C drivers
Bob-the-Kuhn Dec 6, 2023
69da85b
KLEM test error fix - use current macro
Bob-the-Kuhn Dec 6, 2023
218ffa0
fix typo in marlinui_DOGM.h
Bob-the-Kuhn Dec 7, 2023
e1b9a4d
adjust
thinkyhead Dec 9, 2023
3705f77
clean up
thinkyhead Dec 9, 2023
fc98192
single SlowSoftWire
thinkyhead Dec 9, 2023
607d837
followup
thinkyhead Dec 9, 2023
892c347
apply standard
thinkyhead Dec 9, 2023
1f2148e
Allow compile-time optimization
thinkyhead Dec 9, 2023
690fc49
ws
thinkyhead Dec 9, 2023
3a5d488
filter soft i2c libraries
thinkyhead Dec 11, 2023
fdd617a
use proper flag, tweak test
thinkyhead Dec 11, 2023
ad8b91a
conditional
thinkyhead Dec 11, 2023
fad7d7d
remove LCDSCREEN_NAME
thinkyhead Dec 11, 2023
4af6a75
move conditionals, add test
thinkyhead Dec 11, 2023
87a8f14
split up and filter
thinkyhead Dec 12, 2023
d410e6e
fix a warn
thinkyhead Dec 12, 2023
7c34822
headers
thinkyhead Dec 12, 2023
e8189c0
fix flags
thinkyhead Dec 12, 2023
5119dd3
more flags
thinkyhead Dec 12, 2023
2a05fc0
need u8g
thinkyhead Dec 12, 2023
bf79fb9
move extern
thinkyhead Dec 12, 2023
64f6836
get pin_t
thinkyhead Dec 12, 2023
0479483
encoder tweak
thinkyhead Dec 12, 2023
43c0ad2
retry later
thinkyhead Dec 13, 2023
3cbaa5a
Merge branch 'bugfix-2.1.x' into pr/26433
thinkyhead Dec 27, 2023
3d34cc8
merge followup
thinkyhead Dec 27, 2023
da724c1
shared commands
thinkyhead Dec 27, 2023
addf2ad
fix u8g init typo
thinkyhead Jan 4, 2024
7772c7d
Merge branch 'bugfix-2.1.x' into pr/26433
thinkyhead Jan 4, 2024
792edeb
comma
thinkyhead Jan 4, 2024
4b07a32
Merge branch 'bugfix-2.1.x' into pr/26433
thinkyhead Jan 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions Marlin/src/HAL/STM32/u8g_com_stm32duino_ssd_i2c.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2023 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 I2C and software I2C.
*
* The WIRE library is used for the hardware I2C. That means that
* ANY hardware SDA and SLC pairs can be selected and the WIRE library
* will automatically enable the correct hardware.
*
* The SoftWire library is used for the software I2C. That means that
* ANY pin can be selected for SDA and SLC.
*
* This driver requires the SDA and SLC pins be named:
* DOGLCD_SDA
* DOGLCD_SCL
*
* This allows independence from other I2C devices (mostly EEPROMs) that
* usually use I2C_SDA_PIN and I2C_SLC_PIN.
*
* The following MUST be present in the platformio environment used to
* compile Marlin. This required because
*
* The flag LCD_I2C_SOFT is used to select which I2C library is used.
* It must be enabled in the platformio environment used to compile
* Marlin. Enabling it anywhere else results in LCD_I2C_SOFT not being
* enabled during crucial portions of the compile process which results
* in defaulting to hardwired operation. The net result is the following
* MUST be in the platformio environment in order to use the software I2C
* system:
* build_flags = -DLCD_I2C_SOFT
* lib_deps = stevemarple/SoftWire@^2.0.9
* stevemarple/AsyncDelay@^1.1.2
*
*/

#if defined(ARDUINO_ARCH_STM32)
thinkyhead marked this conversation as resolved.
Show resolved Hide resolved

#include <U8glib-HAL.h>

#include "../../MarlinCore.h" // so can get SDA & SCL pins

/*
BUFFER_LENGTH is defined in libraries\Wire\utility\WireBase.h
Default value is 32
Increate this value to 144 to send U8G_COM_MSG_WRITE_SEQ in single block
*/

#if !defined(BUFFER_LENGTH) || BUFFER_LENGTH >= 144
thinkyhead marked this conversation as resolved.
Show resolved Hide resolved
#define BUFFER_LENGTH 32
#endif
#define I2C_MAX_LENGTH (BUFFER_LENGTH - 1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some i2c libraries impose a message length limit of 32 bytes, though this is not universal. What is the reason for making the max message length one less than the buffer length? For a 32-byte buffer it will result in 31 bytes sent followed by one byte, but i2c libraries should at least be able to handle the full 32 byte send.


#ifdef LCD_I2C_SOFT
#include <SoftWire.h>
#include <AsyncDelay.h>

char swTxBuffer[BUFFER_LENGTH];
char swRxBuffer[BUFFER_LENGTH];
SoftWire sw(DOGLCD_SDA, DOGLCD_SCL);
#define I2C_ITF sw

#elif !defined(HAL_I2C_MODULE_DISABLED)
#include <Wire.h>
#define I2C_ITF Wire
#ifndef MASTER_ADDRESS
#define MASTER_ADDRESS 0x01
#endif
#else
#error "unsupported I2C configuration"
#endif


static uint8_t control;
static uint8_t msgInitCount = 2; // Ignore all messages until 2nd U8G_COM_MSG_INIT

uint8_t u8g_com_stm32duino_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
if (msgInitCount) {
if (msg == U8G_COM_MSG_INIT) msgInitCount--;
if (msgInitCount) return -1;
}

switch (msg)
{
case U8G_COM_MSG_INIT:
#ifdef LCD_I2C_SOFT
I2C_ITF.setClock(100000);
Copy link
Contributor

@dbuezas dbuezas Nov 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered maxing out clock speed by default?
The maximum is 500_000 but results in "only" 138kHz in an SKR3 (550MHz).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done but it didn't seem to affect anything. I've set both to 400K but both actuals come in at about 100K. I expected to get more speed on the hardware driver version. I'll definitely investigate.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the experiment of removing all delayMicrosecond calls from the SoftWire library and that made it reach something like 200kHZ, then I replaced all pin operations with platform specific ones and it reached 260kHz.
There is another bunch of optimisations which have a very big impact here.

I2C_ITF.setTxBuffer(swTxBuffer, BUFFER_LENGTH);
I2C_ITF.setRxBuffer(swRxBuffer, BUFFER_LENGTH);
I2C_ITF.begin();
#else
I2C_ITF.setClock(400000);
I2C_ITF.setSCL(DOGLCD_SCL);
I2C_ITF.setSDA(DOGLCD_SDA);
I2C_ITF.begin(MASTER_ADDRESS, 0); // start as master
#endif
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:
I2C_ITF.beginTransmission(0x3c);
I2C_ITF.write(control);
I2C_ITF.write(arg_val);
I2C_ITF.endTransmission();
break;

case U8G_COM_MSG_WRITE_SEQ:
{
uint8_t* dataptr = (uint8_t*)arg_ptr;
#ifdef I2C_MAX_LENGTH
while (arg_val > 0) {
I2C_ITF.beginTransmission(0x3c);
I2C_ITF.write(control);
if (arg_val <= I2C_MAX_LENGTH) {
I2C_ITF.write(dataptr, arg_val);
arg_val = 0;
}
else {
I2C_ITF.write(dataptr, I2C_MAX_LENGTH);
arg_val -= I2C_MAX_LENGTH;
dataptr += I2C_MAX_LENGTH;
}
I2C_ITF.endTransmission();
}
#else
I2C_ITF.beginTransmission(0x3c);
I2C_ITF.write(control);
I2C_ITF.write(dataptr, arg_val);
I2C_ITF.endTransmission();
#endif // I2C_MAX_LENGTH
break;
}

}
return 1;
}

#endif // ARDUINO_ARCH_STM32
10 changes: 9 additions & 1 deletion Marlin/src/lcd/dogm/HAL_LCD_com_defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,21 @@

uint8_t u8g_com_HAL_STM32F1_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);
uint8_t u8g_com_stm32duino_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);
uint8_t u8g_com_stm32duino_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);
#define U8G_COM_HAL_SW_SPI_FN u8g_com_HAL_STM32F1_sw_spi_fn
#define U8G_COM_HAL_HW_SPI_FN u8g_com_stm32duino_hw_spi_fn
#define U8G_COM_ST7920_HAL_SW_SPI u8g_com_std_sw_spi_fn
#define U8G_COM_ST7920_HAL_HW_SPI u8g_com_stm32duino_hw_spi_fn
#define U8G_COM_SSD_I2C_HAL u8g_com_stm32duino_ssd_i2c_fn

#elif defined(ARDUINO_ARCH_STM32)

uint8_t u8g_com_std_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);
uint8_t u8g_com_stm32duino_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);
uint8_t u8g_com_stm32duino_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);
#define U8G_COM_HAL_SW_SPI_FN u8g_com_std_sw_spi_fn
#define U8G_COM_HAL_HW_SPI_FN u8g_com_stm32duino_hw_spi_fn
#define U8G_COM_SSD_I2C_HAL u8g_com_stm32duino_ssd_i2c_fn

#elif defined(ESP32)

Expand All @@ -86,13 +90,17 @@
#ifndef U8G_COM_ST7920_HAL_HW_SPI
#define U8G_COM_ST7920_HAL_HW_SPI u8g_com_arduino_st7920_hw_spi_fn
#endif
#ifndef U8G_COM_SSD_I2C_HAL
#define U8G_COM_SSD_I2C_HAL u8g_com_arduino_ssd_i2c_fn
#endif


// This can't be invoked from the current platformio.ini
#ifdef TARGET_LPC1768
uint8_t u8g_com_HAL_LPC1768_ssd_hw_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);
#endif

#define U8G_COM_SSD_I2C_HAL u8g_com_arduino_ssd_i2c_fn


#elif defined(TARGET_LPC1768)

Expand Down
7 changes: 5 additions & 2 deletions Marlin/src/lcd/dogm/marlinui_DOGM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,11 @@ void MarlinUI::init_lcd() {
OUT_WRITE(LCD_BACKLIGHT_PIN, DISABLED(DELAYED_BACKLIGHT_INIT)); // Illuminate after reset or right away
#endif

#if ANY(MKS_12864OLED, MKS_12864OLED_SSD1306, FYSETC_242_OLED_12864, ZONESTAR_12864OLED, K3D_242_OLED_CONTROLLER)
SET_OUTPUT(LCD_PINS_DC);
//#if ANY(MKS_12864OLED, MKS_12864OLED_SSD1306, FYSETC_242_OLED_12864, ZONESTAR_12864OLED, K3D_242_OLED_CONTROLLER) && !defined(LCD_I2C_SOFT)
#if ANY(MKS_12864OLED, FYSETC_242_OLED_12864, ZONESTAR_12864OLED, K3D_242_OLED_CONTROLLER) && !defined(LCD_I2C_SOFT)
#ifdef LCD_PINS_DC
SET_OUTPUT(LCD_PINS_DC);
#endif
#ifndef LCD_RESET_PIN
#define LCD_RESET_PIN LCD_PINS_RS
#endif
Expand Down
19 changes: 13 additions & 6 deletions Marlin/src/lcd/dogm/marlinui_DOGM.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,16 @@

// 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 ENABLED(LCD_I2C_SOFT)
#define U8G_CLASS U8GLIB_SSD1306_128X64_2X_I2C_2_WIRE // I2C via SOFTWIRE
#else
#define U8G_CLASS U8GLIB_SSD1306_128X64 // 8 stripes
// #define FORCE_SOFT_SPI // SW-SPI
#define U8G_PARAM U8G_I2C_OPT_NONE
#if ENABLED(ALTERNATIVE_LCD)
#define U8G_CLASS U8GLIB_SSD1306_128X64_2X_I2C_2_WIRE // 4 stripes
#else
#define U8G_CLASS U8GLIB_SSD1306_128X64 // 8 stripes
#endif
#endif

#elif ANY(FYSETC_242_OLED_12864, K3D_242_OLED_CONTROLLER)
Expand Down Expand Up @@ -161,7 +165,8 @@
// - or -
// Zonestar SH1106 OLED SPI LCD

#define FORCE_SOFT_SPI // SW-SPI
//#define FORCE_SOFT_SPI // SW-SPI
#define U8G_PARAM U8G_I2C_OPT_NONE
#if ENABLED(ALTERNATIVE_LCD)
#define U8G_CLASS U8GLIB_SH1106_128X64_2X // 4 stripes
#else
Expand Down Expand Up @@ -229,6 +234,8 @@
#ifndef U8G_PARAM
#if ENABLED(FORCE_SOFT_SPI)
#define U8G_PARAM DOGLCD_SCK, DOGLCD_MOSI, DOGLCD_CS, DOGLCD_A0 // SW-SPI
#elif ENABLED(LCD_I2C_SOFT)
#define U8G_PARAM // SW-I2C
#else
#define U8G_PARAM DOGLCD_CS, DOGLCD_A0 // HW-SPI
#endif
Expand Down
30 changes: 20 additions & 10 deletions Marlin/src/lcd/dogm/u8g_dev_ssd1306_sh1106_128x64_I2C.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
#define PAGE_HEIGHT 8

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);

// The sh1106 is compatible to the ssd1306, but is 132x64. 128x64 display area is centered within
// the 132x64.
Expand Down Expand Up @@ -106,8 +107,7 @@ static const uint8_t u8g_dev_sh1106_128x64_data_start_2_wire[] PROGMEM = {
#define SH1106_VCOM_DESEL(N) (0xDB), (N)
#define SH1106_NOOP() (0xE3)

static const uint8_t u8g_dev_sh1106_128x64_init_seq_2_wire[] PROGMEM = {
U8G_ESC_ADR(0), // Initiate command mode
static const uint8_t u8g_dev_sh1106_128x64_init_sequence_2_wire[] PROGMEM = {
SH1106_ON(0), // Display off, sleep mode
SH1106_MUX_RATIO(0x3F), // Mux ratio
SH1106_DISP_OFFS(0), // Display offset
Expand All @@ -127,14 +127,17 @@ static const uint8_t u8g_dev_sh1106_128x64_init_seq_2_wire[] PROGMEM = {
SH1106_CHARGE_PUMP(1), // [2] charge pump setting (P62): 0x14 enable, 0x10 disable
SH1106_SCROLL(0), // 2012-05-27: Deactivate scroll
SH1106_ON(1), // Display on
U8G_ESC_END // End of sequence
};

#define LENGTH_SH1106_SEQ_TOTAL sizeof(u8g_dev_sh1106_128x64_init_sequence_2_wire)
#define LENGTH_SH1106_SEQ_ELEMENT sizeof(u8g_dev_sh1106_128x64_init_sequence_2_wire[0])
#define LENGTH_SH1106_SEQ LENGTH_SH1106_SEQ_TOTAL/LENGTH_SH1106_SEQ_ELEMENT // compile errors if combine these three into one line

uint8_t u8g_dev_sh1106_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg) {
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, LENGTH_SH1106_SEQ, u8g_dev_sh1106_128x64_init_sequence_2_wire);
break;
case U8G_DEV_MSG_STOP:
break;
Expand Down Expand Up @@ -174,16 +177,15 @@ static const uint8_t u8g_dev_ssd1306_128x64_data_start_2_wire[] PROGMEM = {
U8G_ESC_END // end of sequence
};

static const uint8_t u8g_dev_ssd1306_128x64_init_seq_2_wire[] PROGMEM = {
U8G_ESC_ADR(0), // initiate command mode
static const uint8_t u8g_dev_ssd1306_128x64_init_sequence_2_wire[] PROGMEM = {
0x0AE, // display off, sleep mode
0x0A8, 0x03F, // mux ratio
0x0D3, 0x00, // display offset
0x040, // start line
0x0A1, // segment remap a0/a1
0x0C8, // c0: scan dir normal, c8: reverse
0x0DA, 0x012, // com pin HW config, sequential com pin config (bit 4), disable left/right remap (bit 5)
0x081, 0x0CF, // [2] set contrast control
0x081, 0x080, // [2] set contrast control
0x020, 0x002, // 2012-05-27: page addressing mode
0x21, 0, 0x7F, // set column range from 0 through 127
0x22, 0, 7, // set page range from 0 through 7
Expand All @@ -195,14 +197,17 @@ static const uint8_t u8g_dev_ssd1306_128x64_init_seq_2_wire[] PROGMEM = {
0x08D, 0x014, // [2] charge pump setting (p62): 0x014 enable, 0x010 disable
0x02E, // 2012-05-27: Deactivate scroll
0x0AF, // display on
U8G_ESC_END // end of sequence
};

#define LENGTH_SSD1306_SEQ_TOTAL sizeof(u8g_dev_ssd1306_128x64_init_sequence_2_wire)
#define LENGTH_SSD1306_SEQ_ELEMENT sizeof(u8g_dev_ssd1306_128x64_init_sequence_2_wire[0])
#define LENGTH_SSD1306_SEQ LENGTH_SSD1306_SEQ_TOTAL/LENGTH_SSD1306_SEQ_ELEMENT // compile errors if combine these three into one line

uint8_t u8g_dev_ssd1306_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg) {
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, LENGTH_SSD1306_SEQ, u8g_dev_ssd1306_128x64_init_sequence_2_wire);
break;
case U8G_DEV_MSG_STOP:
break;
Expand Down Expand Up @@ -235,7 +240,6 @@ uint8_t u8g_dev_ssd1306_128x64_2x_i2c_2_wire_buf[WIDTH*2] U8G_NOCOMMON ;
u8g_pb_t u8g_dev_ssd1306_128x64_2x_i2c_2_wire_pb = { {16, HEIGHT, 0, 0, 0}, WIDTH, u8g_dev_ssd1306_128x64_2x_i2c_2_wire_buf};
u8g_dev_t u8g_dev_ssd1306_128x64_2x_i2c_2_wire = { u8g_dev_ssd1306_128x64_2x_2_wire_fn, &u8g_dev_ssd1306_128x64_2x_i2c_2_wire_pb, U8G_COM_SSD_I2C_HAL };


/////////////////////////////////////////////////////////////////////////////////////////////

// This routine adds the instruction byte in between the command bytes. This makes the init
Expand Down Expand Up @@ -299,4 +303,10 @@ uint8_t u8g_WriteEscSeqP_2_wire(u8g_t *u8g, u8g_dev_t *dev, const uint8_t *esc_s
return 1;
}

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_MARLINUI_U8GLIB
Loading