diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h
index 0a22283ff075..7faffd1a08c1 100644
--- a/Marlin/Configuration.h
+++ b/Marlin/Configuration.h
@@ -77,7 +77,7 @@
* Serial port -1 is the USB emulated serial port, if available.
* Note: The first serial port (-1 or 0) will always be used by the Arduino bootloader.
*
- * :[-1, 0, 1, 2, 3, 4, 5, 6, 7]
+ * :[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
*/
#define SERIAL_PORT -1
@@ -99,7 +99,7 @@
/**
* Select a secondary serial port on the board to use for communication with the host.
* Currently Ethernet (-2) is only supported on Teensy 4.1 boards.
- * :[-2, -1, 0, 1, 2, 3, 4, 5, 6, 7]
+ * :[-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
*/
//#define SERIAL_PORT_2 -1
//#define BAUDRATE_2 250000 // :[2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000] Enable to override BAUDRATE
@@ -107,11 +107,21 @@
/**
* Select a third serial port on the board to use for communication with the host.
* Currently only supported for AVR, DUE, LPC1768/9 and STM32/STM32F1
- * :[-1, 0, 1, 2, 3, 4, 5, 6, 7]
+ * :[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
*/
//#define SERIAL_PORT_3 1
//#define BAUDRATE_3 250000 // :[2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000] Enable to override BAUDRATE
+/**
+ * Select a serial port to communicate with RS485 protocol
+ * :[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ */
+//#define RS485_SERIAL_PORT 1
+#ifdef RS485_SERIAL_PORT
+ //#define M485_PROTOCOL 1 // Check your host for protocol compatibility
+ //#define RS485_BUS_BUFFER_SIZE 128
+#endif
+
// Enable the Bluetooth serial interface on AT90USB devices
//#define BLUETOOTH
diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h
index d95c8ca828cd..17d26ee81bfc 100644
--- a/Marlin/Configuration_adv.h
+++ b/Marlin/Configuration_adv.h
@@ -603,7 +603,9 @@
* (Does not work on Sanguinololu with FAN_SOFT_PWM.)
*/
#define FAN_KICKSTART_TIME 200 // (ms)
-#define FAN_KICKSTART_POWER 200 // 64-255
+#define FAN_KICKSTART_POWER 255 // 64-255
+#define FAN_KICKSTART_LINEAR // Set kickstart time linearly based on the speed, e.g., for 20% (51) it will be FAN_KICKSTART_TIME * 0.2.
+ // Useful for quick speed up to low speed. Kickstart power must be set to 255.
// Some coolers may require a non-zero "off" state.
//#define FAN_OFF_PWM 1
diff --git a/Marlin/Version.h b/Marlin/Version.h
index 9922ba35a5db..c76fd3814a5a 100644
--- a/Marlin/Version.h
+++ b/Marlin/Version.h
@@ -41,7 +41,7 @@
* here we define this default string as the date where the latest release
* version was tagged.
*/
-//#define STRING_DISTRIBUTION_DATE "2024-07-02"
+//#define STRING_DISTRIBUTION_DATE "2024-07-12"
/**
* Defines a generic printer name to be output to the LCD after booting Marlin.
diff --git a/Marlin/src/HAL/ESP32/timers.h b/Marlin/src/HAL/ESP32/timers.h
index aa4e1551f066..3f336fbfc962 100644
--- a/Marlin/src/HAL/ESP32/timers.h
+++ b/Marlin/src/HAL/ESP32/timers.h
@@ -53,12 +53,11 @@ typedef uint64_t hal_timer_t;
#if ENABLED(I2S_STEPPER_STREAM)
#define STEPPER_TIMER_PRESCALE 1
#define STEPPER_TIMER_RATE 250000 // 250khz, 4µs pulses of i2s word clock
- #define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) // stepper timer ticks per µs // wrong would be 0.25
#else
#define STEPPER_TIMER_PRESCALE 40
#define STEPPER_TIMER_RATE ((HAL_TIMER_RATE) / (STEPPER_TIMER_PRESCALE)) // frequency of stepper timer, 2MHz
- #define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) // stepper timer ticks per µs
#endif
+#define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) // stepper timer ticks per µs
#define STEP_TIMER_MIN_INTERVAL 8 // minimum time in µs between stepper interrupts
diff --git a/Marlin/src/HAL/STM32/HAL.h b/Marlin/src/HAL/STM32/HAL.h
index 9c74a95ff0c4..e25ca27d8eda 100644
--- a/Marlin/src/HAL/STM32/HAL.h
+++ b/Marlin/src/HAL/STM32/HAL.h
@@ -117,6 +117,14 @@
#endif
#endif
+#ifdef RS485_SERIAL_PORT
+ #if WITHIN(RS485_SERIAL_PORT, 1, 9)
+ #define RS485_SERIAL MSERIAL(RS485_SERIAL_PORT)
+ #else
+ #error "RS485_SERIAL_PORT must be from 1 to 9."
+ #endif
+#endif
+
/**
* TODO: review this to return 1 for pins that are not analog input
*/
diff --git a/Marlin/src/HAL/STM32/MarlinSerial.cpp b/Marlin/src/HAL/STM32/MarlinSerial.cpp
index c4bc62994908..862678373fb1 100644
--- a/Marlin/src/HAL/STM32/MarlinSerial.cpp
+++ b/Marlin/src/HAL/STM32/MarlinSerial.cpp
@@ -37,6 +37,9 @@
#ifndef USART5
#define USART5 UART5
#endif
+#ifndef USART6
+ #define USART6 UART6
+#endif
#ifndef USART7
#define USART7 UART7
#endif
diff --git a/Marlin/src/HAL/STM32F1/HAL.h b/Marlin/src/HAL/STM32F1/HAL.h
index 007bf83b0974..c422f60500b6 100644
--- a/Marlin/src/HAL/STM32F1/HAL.h
+++ b/Marlin/src/HAL/STM32F1/HAL.h
@@ -143,6 +143,17 @@
#endif
#endif
+#ifdef RS485_SERIAL_PORT
+ #if RS485_SERIAL_PORT == -1
+ #define RS485_SERIAL UsbSerial
+ #elif WITHIN(RS485_SERIAL_PORT, 1, NUM_UARTS)
+ #define RS485_SERIAL MSERIAL(RS485_SERIAL_PORT)
+ #else
+ #define RS485_SERIAL MSERIAL(1) // dummy port
+ static_assert(false, "RS485_SERIAL_PORT must be from 1 to " STRINGIFY(NUM_UARTS) ".")
+ #endif
+#endif
+
/**
* TODO: review this to return 1 for pins that are not analog input
*/
diff --git a/Marlin/src/HAL/TEENSY31_32/timers.h b/Marlin/src/HAL/TEENSY31_32/timers.h
index 9fcbb6f232c9..3bbc2421e006 100644
--- a/Marlin/src/HAL/TEENSY31_32/timers.h
+++ b/Marlin/src/HAL/TEENSY31_32/timers.h
@@ -41,7 +41,7 @@ typedef uint32_t hal_timer_t;
#define FTM0_TIMER_PRESCALE_BITS 0b011
#define FTM1_TIMER_PRESCALE_BITS 0b010
-#define FTM0_TIMER_RATE (F_BUS / (FTM0_TIMER_PRESCALE)) // 60MHz / 8 = 7500kHz
+#define FTM0_TIMER_RATE (F_BUS / (FTM0_TIMER_PRESCALE)) // 60MHz / 8 = 7.5MHz
#define FTM1_TIMER_RATE (F_BUS / (FTM1_TIMER_PRESCALE)) // 60MHz / 4 = 15MHz
#define HAL_TIMER_RATE (FTM0_TIMER_RATE)
diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp
index be81388f3896..c9a0a5432ec7 100644
--- a/Marlin/src/MarlinCore.cpp
+++ b/Marlin/src/MarlinCore.cpp
@@ -261,9 +261,13 @@
#include "tests/marlin_tests.h"
#endif
+#if HAS_RS485_SERIAL
+ #include "feature/rs485.h"
+#endif
+
PGMSTR(M112_KILL_STR, "M112 Shutdown");
-MarlinState marlin_state = MF_INITIALIZING;
+MarlinState marlin_state = MarlinState::MF_INITIALIZING;
// For M109 and M190, this flag may be cleared (by M108) to exit the wait loop
bool wait_for_heatup = false;
@@ -377,8 +381,8 @@ void startOrResumeJob() {
}
inline void finishSDPrinting() {
- if (queue.enqueue_one(F("M1001"))) { // Keep trying until it gets queued
- marlin_state = MF_RUNNING; // Signal to stop trying
+ if (queue.enqueue_one(F("M1001"))) { // Keep trying until it gets queued
+ marlin_state = MarlinState::MF_RUNNING; // Signal to stop trying
TERN_(PASSWORD_AFTER_SD_PRINT_END, password.lock_machine());
TERN_(DGUS_LCD_UI_MKS, screen.sdPrintingFinished());
}
@@ -773,7 +777,7 @@ void idle(const bool no_stepper_sleep/*=false*/) {
TERN_(MAX7219_DEBUG, max7219.idle_tasks());
// Return if setup() isn't completed
- if (marlin_state == MF_INITIALIZING) goto IDLE_DONE;
+ if (marlin_state == MarlinState::MF_INITIALIZING) goto IDLE_DONE;
// TODO: Still causing errors
TERN_(TOOL_SENSOR, (void)check_tool_sensor_stats(active_extruder, true));
@@ -959,7 +963,7 @@ void stop() {
SERIAL_ERROR_MSG(STR_ERR_STOPPED);
LCD_MESSAGE(MSG_STOPPED);
safe_delay(350); // allow enough time for messages to get out before stopping
- marlin_state = MF_STOPPED;
+ marlin_state = MarlinState::MF_STOPPED;
}
}
@@ -1273,7 +1277,7 @@ void setup() {
// Identify myself as Marlin x.x.x
SERIAL_ECHOLNPGM("Marlin " SHORT_BUILD_VERSION);
- #if defined(STRING_DISTRIBUTION_DATE) && defined(STRING_CONFIG_H_AUTHOR)
+ #ifdef STRING_DISTRIBUTION_DATE
SERIAL_ECHO_MSG(
" Last Updated: " STRING_DISTRIBUTION_DATE
" | Author: " STRING_CONFIG_H_AUTHOR
@@ -1642,11 +1646,15 @@ void setup() {
SETUP_RUN(bdl.init(I2C_BD_SDA_PIN, I2C_BD_SCL_PIN, I2C_BD_DELAY));
#endif
+ #if HAS_RS485_SERIAL
+ SETUP_RUN(rs485_init());
+ #endif
+
#if ENABLED(FT_MOTION)
SETUP_RUN(ftMotion.init());
#endif
- marlin_state = MF_RUNNING;
+ marlin_state = MarlinState::MF_RUNNING;
#ifdef STARTUP_TUNE
// Play a short startup tune before continuing.
@@ -1678,7 +1686,7 @@ void loop() {
#if HAS_MEDIA
if (card.flag.abort_sd_printing) abortSDPrinting();
- if (marlin_state == MF_SD_COMPLETE) finishSDPrinting();
+ if (marlin_state == MarlinState::MF_SD_COMPLETE) finishSDPrinting();
#endif
queue.advance();
diff --git a/Marlin/src/MarlinCore.h b/Marlin/src/MarlinCore.h
index e9c63bb31f35..9cf74de2c68f 100644
--- a/Marlin/src/MarlinCore.h
+++ b/Marlin/src/MarlinCore.h
@@ -42,7 +42,7 @@ void kill(FSTR_P const lcd_error=nullptr, FSTR_P const lcd_component=nullptr, co
void minkill(const bool steppers_off=false);
// Global State of the firmware
-enum MarlinState : uint8_t {
+enum class MarlinState : uint8_t {
MF_INITIALIZING = 0,
MF_STOPPED,
MF_KILLED,
@@ -53,8 +53,8 @@ enum MarlinState : uint8_t {
};
extern MarlinState marlin_state;
-inline bool IsRunning() { return marlin_state >= MF_RUNNING; }
-inline bool IsStopped() { return marlin_state == MF_STOPPED; }
+inline bool IsRunning() { return marlin_state >= MarlinState::MF_RUNNING; }
+inline bool IsStopped() { return marlin_state == MarlinState::MF_STOPPED; }
bool printingIsActive();
bool printJobOngoing();
diff --git a/Marlin/src/feature/backlash.cpp b/Marlin/src/feature/backlash.cpp
index 07fa7725a06c..3b9d78cb2e89 100644
--- a/Marlin/src/feature/backlash.cpp
+++ b/Marlin/src/feature/backlash.cpp
@@ -171,13 +171,14 @@ int32_t Backlash::get_applied_steps(const AxisEnum axis) {
const int32_t residual_error_axis = residual_error[axis];
- // At startup it is assumed the last move was forward.
- // So the applied steps will always be negative.
+ // At startup, when no steps are applied, it is assumed the last move was backwards.
+ // So the applied steps will always be zero (when moving backwards) or a positive
+ // number (when moving forwards).
- if (forward) return -residual_error_axis;
+ if (!forward) return -residual_error_axis;
const float f_corr = float(correction) / all_on;
- const int32_t full_error_axis = -f_corr * distance_mm[axis] * planner.settings.axis_steps_per_mm[axis];
+ const int32_t full_error_axis = f_corr * distance_mm[axis] * planner.settings.axis_steps_per_mm[axis];
return full_error_axis - residual_error_axis;
}
diff --git a/Marlin/src/feature/ethernet.cpp b/Marlin/src/feature/ethernet.cpp
index c5bfa932cb5b..9b022b4e17e3 100644
--- a/Marlin/src/feature/ethernet.cpp
+++ b/Marlin/src/feature/ethernet.cpp
@@ -141,7 +141,7 @@ void MarlinEthernet::check() {
case CONNECTING:
telnetClient.println("Marlin " SHORT_BUILD_VERSION);
- #if defined(STRING_DISTRIBUTION_DATE) && defined(STRING_CONFIG_H_AUTHOR)
+ #ifdef STRING_DISTRIBUTION_DATE
telnetClient.println(
" Last Updated: " STRING_DISTRIBUTION_DATE
" | Author: " STRING_CONFIG_H_AUTHOR
diff --git a/Marlin/src/feature/leds/leds.cpp b/Marlin/src/feature/leds/leds.cpp
index ac7f1815162b..8810c518cf70 100644
--- a/Marlin/src/feature/leds/leds.cpp
+++ b/Marlin/src/feature/leds/leds.cpp
@@ -239,7 +239,7 @@ void LEDLights::set_color(const LEDColor &incol
void LEDLights::toggle() { if (lights_on) set_off(); else update(); }
#endif
-#if LED_POWEROFF_TIMEOUT > 0
+#if HAS_LED_POWEROFF_TIMEOUT
millis_t LEDLights::led_off_time; // = 0
diff --git a/Marlin/src/feature/leds/leds.h b/Marlin/src/feature/leds/leds.h
index 7a31ca685d49..da8ba42fba1a 100644
--- a/Marlin/src/feature/leds/leds.h
+++ b/Marlin/src/feature/leds/leds.h
@@ -164,11 +164,11 @@ class LEDLights {
#if ENABLED(LED_CONTROL_MENU)
static void toggle(); // swap "off" with color
#endif
- #if ANY(LED_CONTROL_MENU, CASE_LIGHT_USE_RGB_LED) || LED_POWEROFF_TIMEOUT > 0
+ #if ANY(LED_CONTROL_MENU, CASE_LIGHT_USE_RGB_LED, HAS_LED_POWEROFF_TIMEOUT)
static void update() { set_color(color); }
#endif
- #if LED_POWEROFF_TIMEOUT > 0
+ #if HAS_LED_POWEROFF_TIMEOUT
private:
static millis_t led_off_time;
public:
diff --git a/Marlin/src/feature/power.cpp b/Marlin/src/feature/power.cpp
index 20eb63a6f160..c6dc56283692 100644
--- a/Marlin/src/feature/power.cpp
+++ b/Marlin/src/feature/power.cpp
@@ -201,7 +201,7 @@ void Power::power_off() {
/**
* Check all conditions that would signal power needing to be on.
*
- * @returns bool if power is needed
+ * @return bool if power is needed
*/
bool Power::is_power_needed() {
diff --git a/Marlin/src/feature/rs485.cpp b/Marlin/src/feature/rs485.cpp
new file mode 100644
index 000000000000..800918545081
--- /dev/null
+++ b/Marlin/src/feature/rs485.cpp
@@ -0,0 +1,39 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2024 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 .
+ *
+ */
+#include "../inc/MarlinConfig.h"
+
+#if HAS_RS485_SERIAL
+
+#include "rs485.h"
+
+HardwareSerialBusIO rs485BusIO(&RS485_SERIAL);
+RS485Bus rs485Bus(rs485BusIO, RS485_RX_ENABLE_PIN, RS485_TX_ENABLE_PIN);
+
+PhotonProtocol rs485Protocol;
+
+Packetizer rs485Packetizer(rs485Bus, rs485Protocol);
+
+uint8_t rs485Buffer[RS485_SEND_BUFFER_SIZE];
+
+void rs485_init() { RS485_SERIAL.begin(57600); }
+
+#endif // HAS_RS485_SERIAL
diff --git a/Marlin/src/feature/rs485.h b/Marlin/src/feature/rs485.h
new file mode 100644
index 000000000000..3327626a3c05
--- /dev/null
+++ b/Marlin/src/feature/rs485.h
@@ -0,0 +1,40 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2024 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 .
+ *
+ */
+#pragma once
+
+#include "../inc/MarlinConfigPre.h"
+
+#include
+#include
+
+#include
+#include
+
+#define RS485_SEND_BUFFER_SIZE 32
+
+extern HardwareSerialBusIO rs485BusIO;
+extern RS485Bus rs485Bus;
+extern PhotonProtocol rs485Protocol;
+extern Packetizer rs485Packetizer;
+extern uint8_t rs485Buffer[RS485_SEND_BUFFER_SIZE];
+
+void rs485_init();
diff --git a/Marlin/src/gcode/calibrate/G33.cpp b/Marlin/src/gcode/calibrate/G33.cpp
index 59e0db132a71..12aed8631663 100644
--- a/Marlin/src/gcode/calibrate/G33.cpp
+++ b/Marlin/src/gcode/calibrate/G33.cpp
@@ -634,7 +634,7 @@ void GcodeSuite::G33() {
}
SERIAL_EOL();
- MString<20> msg(F("Calibration sd:"));
+ MString<21> msg(F("Calibration sd:"));
if (zero_std_dev_min < 1)
msg.appendf(F("0.%03i"), (int)LROUND(zero_std_dev_min * 1000.0f));
else
diff --git a/Marlin/src/gcode/control/M999.cpp b/Marlin/src/gcode/control/M999.cpp
index b7d6db9f2312..b4278fccad27 100644
--- a/Marlin/src/gcode/control/M999.cpp
+++ b/Marlin/src/gcode/control/M999.cpp
@@ -36,7 +36,7 @@
* existing command buffer.
*/
void GcodeSuite::M999() {
- marlin_state = MF_RUNNING;
+ marlin_state = MarlinState::MF_RUNNING;
ui.reset_alert_level();
if (parser.boolval('S')) return;
diff --git a/Marlin/src/gcode/feature/rs485/M485.cpp b/Marlin/src/gcode/feature/rs485/M485.cpp
new file mode 100644
index 000000000000..03640a034eec
--- /dev/null
+++ b/Marlin/src/gcode/feature/rs485/M485.cpp
@@ -0,0 +1,127 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2024 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 .
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if HAS_RS485_SERIAL
+
+#include "../../../feature/rs485.h"
+#include "../../gcode.h"
+
+void write_packet_data() {
+
+ Packet packet = rs485Packetizer.getPacket();
+ for (size_t i = packet.startIndex; i <= packet.endIndex; i++) {
+ const uint8_t data = rs485Bus[i];
+ if (data < 0x10) SERIAL_ECHOPGM_P(PSTR("0"));
+ SERIAL_PRINT(data, PrintBase::Hex);
+ }
+
+ SERIAL_EOL();
+}
+
+static void rs485_write_failed(const PacketWriteResult writeResult) {
+ SERIAL_ERROR_START();
+ SERIAL_ECHOPGM("RS485: Write failed ");
+ switch (writeResult) {
+ case PacketWriteResult::FAILED_INTERRUPTED: SERIAL_ECHOPGM("interrupted"); break;
+ case PacketWriteResult::FAILED_BUFFER_FULL: SERIAL_ECHOPGM("buffer full"); break;
+ case PacketWriteResult::FAILED_TIMEOUT: SERIAL_ECHOPGM("timeout"); break;
+ }
+ SERIAL_EOL();
+}
+
+void GcodeSuite::M485() {
+ if (strlen(parser.string_arg) & 1) {
+ SERIAL_ERROR_MSG("String must contain an even number of bytes.");
+ return;
+ }
+
+ if (strlen(parser.string_arg) > RS485_SEND_BUFFER_SIZE * 2) {
+ SERIAL_ERROR_MSG("String too long (" STRINGIFY(RS485_SEND_BUFFER_SIZE) " bytes max).");
+ return;
+ }
+
+ // Convert the string to bytes in the buffer
+ for (size_t i = 0; i < strlen(parser.string_arg); i += 2) {
+ const uint8_t nybble1 = HEXCHR(parser.string_arg[i]),
+ nybble2 = HEXCHR(parser.string_arg[i + 1]);
+
+ if (nybble1 == -1 || nybble2 == -1) {
+ SERIAL_ERROR_START();
+ SERIAL_ECHOPGM("Not a hex character: ");
+ SERIAL_CHAR(nybble1 == -1 ? parser.string_arg[i] : parser.string_arg[i+1]);
+ SERIAL_EOL();
+ return;
+ }
+
+ rs485Buffer[i >> 1] = (nybble1 & 0x0F) << 4 | (nybble2 & 0x0F);
+ }
+
+ rs485Packetizer.setMaxReadTimeout(50000); // This can be super small since ideally any packets will already be in our buffer
+ rs485Packetizer.setFalsePacketVerificationTimeout(5000);
+
+ // Read and ignore any packets that may have come in, before we write.
+
+ while (rs485Packetizer.hasPacket()) {
+ #if M485_PROTOCOL >= 2
+ SERIAL_ECHO_START();
+ #endif
+ SERIAL_ECHO(F("rs485-"), F("unexpected-packet: "));
+ write_packet_data();
+ rs485Packetizer.clearPacket();
+ }
+
+ const PacketWriteResult writeResult = rs485Packetizer.writePacket(rs485Buffer, strlen(parser.string_arg) / 2);
+ switch (writeResult) {
+ default: rs485_write_failed(writeResult);
+ case PacketWriteResult::OK: break; // Nothing to do
+ }
+
+ //millis_t startTime = millis();
+ bool hasPacket = rs485Packetizer.hasPacket();
+ //millis_t endTime = millis();
+ //#if M485_PROTOCOL >= 2
+ // SERIAL_ECHO_START();
+ //#endif
+ //SERIAL_ECHOLN(F("rs485-"), F("time: "), endTime - startTime);
+
+ #if M485_PROTOCOL >= 2
+ SERIAL_ECHO_START();
+ #endif
+
+ SERIAL_ECHO(F("rs485-"));
+ if (!hasPacket) {
+ #if M485_PROTOCOL >= 2
+ SERIAL_ECHOLN(F("timeout"));
+ #else
+ SERIAL_ECHOLN(F("reply: TIMEOUT"));
+ #endif
+ return;
+ }
+
+ SERIAL_ECHO(F("reply: "));
+ write_packet_data();
+ rs485Packetizer.clearPacket();
+}
+
+#endif // HAS_RS485_SERIAL
diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp
index 9e81c7d4bc4b..2a8bfa4e662a 100644
--- a/Marlin/src/gcode/gcode.cpp
+++ b/Marlin/src/gcode/gcode.cpp
@@ -897,6 +897,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
case 430: M430(); break; // M430: Read the system current (A), voltage (V), and power (W)
#endif
+ #if HAS_RS485_SERIAL
+ case 485: M485(); break; // M485: Send RS485 packets
+ #endif
+
#if ENABLED(CANCEL_OBJECTS)
case 486: M486(); break; // M486: Identify and cancel objects
#endif
diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h
index 144d724cd9cd..bb2f0099949b 100644
--- a/Marlin/src/gcode/gcode.h
+++ b/Marlin/src/gcode/gcode.h
@@ -243,6 +243,7 @@
* M425 - Enable/Disable and tune backlash correction. (Requires BACKLASH_COMPENSATION and BACKLASH_GCODE)
* M428 - Set the home_offset based on the current_position. Nearest edge applies. (Disabled by NO_WORKSPACE_OFFSETS or DELTA)
* M430 - Read the system current, voltage, and power (Requires POWER_MONITOR_CURRENT, POWER_MONITOR_VOLTAGE, or POWER_MONITOR_FIXED_VOLTAGE)
+ * M485 - Send RS485 packets (Requires RS485_SERIAL_PORT)
* M486 - Identify and cancel objects. (Requires CANCEL_OBJECTS)
* M500 - Store parameters in EEPROM. (Requires EEPROM_SETTINGS)
* M501 - Restore parameters from EEPROM. (Requires EEPROM_SETTINGS)
@@ -1063,6 +1064,10 @@ class GcodeSuite {
static void M430();
#endif
+ #if HAS_RS485_SERIAL
+ static void M485();
+ #endif
+
#if ENABLED(CANCEL_OBJECTS)
static void M486();
#endif
diff --git a/Marlin/src/gcode/parser.cpp b/Marlin/src/gcode/parser.cpp
index 16c3b2d9bd0b..4b8bbb925fde 100644
--- a/Marlin/src/gcode/parser.cpp
+++ b/Marlin/src/gcode/parser.cpp
@@ -274,9 +274,12 @@ void GCodeParser::parse(char *p) {
// Only use string_arg for these M codes
if (letter == 'M') switch (codenum) {
- TERN_(GCODE_MACROS, case 810 ... 819:)
TERN_(EXPECTED_PRINTER_CHECK, case 16:)
- case 23: case 28: case 30: case 117 ... 118: case 928:
+ TERN_(SDSUPPORT, case 23: case 28: case 30: case 928:)
+ TERN_(HAS_STATUS_MESSAGE, case 117:)
+ TERN_(HAS_RS485_SERIAL, case 485:)
+ TERN_(GCODE_MACROS, case 810 ... 819:)
+ case 118:
string_arg = unescape_string(p);
return;
default: break;
diff --git a/Marlin/src/inc/Conditionals_LCD.h b/Marlin/src/inc/Conditionals_LCD.h
index 7de6146f293c..573cfb0d3c75 100644
--- a/Marlin/src/inc/Conditionals_LCD.h
+++ b/Marlin/src/inc/Conditionals_LCD.h
@@ -26,6 +26,10 @@
* Conditionals that need to be set before Configuration_adv.h or pins.h
*/
+#ifndef STRING_CONFIG_H_AUTHOR
+ #define STRING_CONFIG_H_AUTHOR "(anonymous)"
+#endif
+
/**
* Extruders have some combination of stepper motors and hotends
* so we separate these concepts into the defines:
@@ -654,11 +658,11 @@
#elif ENABLED(ZONESTAR_12864OLED)
#define IS_RRD_SC 1
- #define U8GLIB_SH1106
+ #define U8GLIB_SH1106_SPI
#elif ENABLED(ZONESTAR_12864OLED_SSD1306)
#define IS_RRD_SC 1
- #define IS_U8GLIB_SSD1306
+ #define U8GLIB_SSD1306_SPI
#elif ENABLED(RADDS_DISPLAY)
#define IS_ULTIPANEL 1
@@ -716,7 +720,7 @@
#elif ENABLED(SAV_3DGLCD)
- #ifdef U8GLIB_SSD1306
+ #if ENABLED(U8GLIB_SSD1306)
#define IS_U8GLIB_SSD1306 // Allow for U8GLIB_SSD1306 + SAV_3DGLCD
#endif
#define IS_NEWPANEL 1
@@ -857,9 +861,12 @@
#define STD_ENCODER_STEPS_PER_MENU_ITEM 1
#endif
-// 128x64 I2C OLED LCDs - SSD1306/SSD1309/SH1106
+// 128x64 I2C OLED LCDs (SSD1306 / SSD1309 / SH1106)
+// ...and 128x64 SPI OLED LCDs (SSD1306 / SH1106)
#if ANY(U8GLIB_SSD1306, U8GLIB_SSD1309, U8GLIB_SH1106)
#define HAS_U8GLIB_I2C_OLED 1
+#endif
+#if ANY(HAS_U8GLIB_I2C_OLED, U8GLIB_SSD1306_SPI, U8GLIB_SH1106_SPI)
#define HAS_WIRED_LCD 1
#define DOGLCD
#endif
@@ -1666,6 +1673,9 @@
#if SERIAL_PORT == -1 || SERIAL_PORT_2 == -1 || SERIAL_PORT_3 == -1
#define HAS_USB_SERIAL 1
#endif
+#ifdef RS485_SERIAL_PORT
+ #define HAS_RS485_SERIAL 1
+#endif
#if SERIAL_PORT_2 == -2
#define HAS_ETHERNET 1
#endif
@@ -1895,6 +1905,10 @@
#define NEOPIXEL_BKGD_INDEX_LAST NEOPIXEL_BKGD_INDEX_FIRST
#endif
+#if LED_POWEROFF_TIMEOUT > 0
+ #define HAS_LED_POWEROFF_TIMEOUT 1
+#endif
+
#if ALL(SPI_FLASH, HAS_MEDIA, MARLIN_DEV_MODE)
#define SPI_FLASH_BACKUP 1
#endif
diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h
index d1524cf82a12..e6f307877d0f 100644
--- a/Marlin/src/inc/Conditionals_post.h
+++ b/Marlin/src/inc/Conditionals_post.h
@@ -1827,11 +1827,12 @@
//
// Flag the indexed hardware serial ports in use
-#define SERIAL_IN_USE(N) ( (defined(SERIAL_PORT) && N == SERIAL_PORT) \
- || (defined(SERIAL_PORT_2) && N == SERIAL_PORT_2) \
- || (defined(SERIAL_PORT_3) && N == SERIAL_PORT_3) \
- || (defined(MMU2_SERIAL_PORT) && N == MMU2_SERIAL_PORT) \
- || (defined(LCD_SERIAL_PORT) && N == LCD_SERIAL_PORT) )
+#define SERIAL_IN_USE(N) ( (defined(SERIAL_PORT) && N == SERIAL_PORT) \
+ || (defined(SERIAL_PORT_2) && N == SERIAL_PORT_2) \
+ || (defined(SERIAL_PORT_3) && N == SERIAL_PORT_3) \
+ || (defined(MMU2_SERIAL_PORT) && N == MMU2_SERIAL_PORT) \
+ || (defined(LCD_SERIAL_PORT) && N == LCD_SERIAL_PORT) \
+ || (defined(RS485_SERIAL_PORT) && N == RS485_SERIAL_PORT) )
// Flag the named hardware serial ports in use
#define TMC_UART_IS(A,N) (defined(A##_HARDWARE_SERIAL) && (CAT(HW_,A##_HARDWARE_SERIAL) == HW_Serial##N || CAT(HW_,A##_HARDWARE_SERIAL) == HW_MSerial##N))
@@ -2757,7 +2758,7 @@
// Fan Kickstart
#if FAN_KICKSTART_TIME && !defined(FAN_KICKSTART_POWER)
- #define FAN_KICKSTART_POWER 180
+ #define FAN_KICKSTART_POWER TERN(FAN_KICKSTART_LINEAR, 255, 180)
#endif
// Servos
diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h
index f1101461d959..b13377cf5f9e 100644
--- a/Marlin/src/inc/SanityCheck.h
+++ b/Marlin/src/inc/SanityCheck.h
@@ -1009,8 +1009,12 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i
#endif
// Fan Kickstart power
-#if FAN_KICKSTART_TIME && !WITHIN(FAN_KICKSTART_POWER, 64, 255)
- #error "FAN_KICKSTART_POWER must be an integer from 64 to 255."
+#if FAN_KICKSTART_TIME
+ #if ENABLED(FAN_KICKSTART_LINEAR) && FAN_KICKSTART_POWER != 255
+ #error "FAN_KICKSTART_LINEAR requires a FAN_KICKSTART_POWER of 255."
+ #elif !WITHIN(FAN_KICKSTART_POWER, 64, 255)
+ #error "FAN_KICKSTART_POWER must be an integer from 64 to 255."
+ #endif
#endif
/**
@@ -2915,6 +2919,8 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i
#error "MMU2_SERIAL_PORT cannot be the same as SERIAL_PORT_2."
#elif defined(LCD_SERIAL_PORT) && MMU2_SERIAL_PORT == LCD_SERIAL_PORT
#error "MMU2_SERIAL_PORT cannot be the same as LCD_SERIAL_PORT."
+ #elif defined(RS485_SERIAL_PORT) && MMU2_SERIAL_PORT == RS485_SERIAL_PORT
+ #error "MMU2_SERIAL_PORT cannot be the same as RS485_SERIAL_PORT."
#endif
#endif
@@ -2926,6 +2932,8 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i
#error "LCD_SERIAL_PORT cannot be the same as SERIAL_PORT."
#elif defined(SERIAL_PORT_2) && LCD_SERIAL_PORT == SERIAL_PORT_2
#error "LCD_SERIAL_PORT cannot be the same as SERIAL_PORT_2."
+ #elif defined(RS485_SERIAL_PORT) && LCD_SERIAL_PORT == RS485_SERIAL_PORT
+ #error "LCD_SERIAL_PORT cannot be the same as RS485_SERIAL_PORT."
#endif
#else
#if HAS_DGUS_LCD
@@ -2939,6 +2947,17 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i
#endif
#endif
+/**
+ * RS485 bus requires a dedicated serial port
+ */
+#ifdef RS485_SERIAL_PORT
+ #if RS485_SERIAL_PORT == SERIAL_PORT
+ #error "RS485_SERIAL_PORT cannot be the same as SERIAL_PORT."
+ #elif defined(SERIAL_PORT_2) && RS485_SERIAL_PORT == SERIAL_PORT_2
+ #error "RS485_SERIAL_PORT cannot be the same as SERIAL_PORT_2."
+ #endif
+#endif
+
/**
* Check existing CS pins against enabled TMC SPI drivers.
*/
diff --git a/Marlin/src/inc/Version.h b/Marlin/src/inc/Version.h
index c06e9ca2aaae..38b96cb847e3 100644
--- a/Marlin/src/inc/Version.h
+++ b/Marlin/src/inc/Version.h
@@ -42,7 +42,7 @@
* version was tagged.
*/
#ifndef STRING_DISTRIBUTION_DATE
- #define STRING_DISTRIBUTION_DATE "2024-07-02"
+ #define STRING_DISTRIBUTION_DATE "2024-07-12"
#endif
/**
diff --git a/Marlin/src/lcd/dogm/marlinui_DOGM.cpp b/Marlin/src/lcd/dogm/marlinui_DOGM.cpp
index eed2bf22751a..5e5267c95a40 100644
--- a/Marlin/src/lcd/dogm/marlinui_DOGM.cpp
+++ b/Marlin/src/lcd/dogm/marlinui_DOGM.cpp
@@ -392,7 +392,7 @@ void MarlinUI::clear_for_drawing() {
#if HAS_DISPLAY_SLEEP
void MarlinUI::sleep_display(const bool sleep/*=true*/) {
static bool asleep = false;
- if (asleep != sleep){
+ if (asleep != sleep) {
sleep ? u8g.sleepOn() : u8g.sleepOff();
asleep = sleep;
}
diff --git a/Marlin/src/lcd/dogm/marlinui_DOGM.h b/Marlin/src/lcd/dogm/marlinui_DOGM.h
index 9025395f5600..d3f38cd61994 100644
--- a/Marlin/src/lcd/dogm/marlinui_DOGM.h
+++ b/Marlin/src/lcd/dogm/marlinui_DOGM.h
@@ -155,7 +155,11 @@
#if ENABLED(ALTERNATIVE_LCD)
#define U8G_CLASS U8GLIB_SH1306_128X64_2X // 4 stripes
#else
- #define U8G_CLASS U8GLIB_SH1306_128X64 // 8 stripes
+ #if ENABLED(U8GLIB_SSD1306_SPI)
+ #define U8G_CLASS U8GLIB_SSD1306_128X64_SW_SPI_HAL
+ #else
+ #define U8G_CLASS U8GLIB_SH1306_128X64 // 8 stripes
+ #endif
#endif
#elif ANY(MKS_12864OLED, ZONESTAR_12864OLED)
@@ -168,7 +172,11 @@
#if ENABLED(ALTERNATIVE_LCD)
#define U8G_CLASS U8GLIB_SH1106_128X64_2X // 4 stripes
#else
- #define U8G_CLASS U8GLIB_SH1106_128X64 // 8 stripes
+ #if ENABLED(U8GLIB_SH1106_SPI)
+ #define U8G_CLASS U8GLIB_SH1106_128X64_SW_SPI_HAL
+ #else
+ #define U8G_CLASS U8GLIB_SH1106_128X64 // 8 stripes
+ #endif
#endif
#elif ENABLED(U8GLIB_SH1106_EINSTART)
diff --git a/Marlin/src/lcd/dogm/u8g/HAL_LCD_class_defines.h b/Marlin/src/lcd/dogm/u8g/HAL_LCD_class_defines.h
index 907fa43c9bd7..33a9c21c4109 100644
--- a/Marlin/src/lcd/dogm/u8g/HAL_LCD_class_defines.h
+++ b/Marlin/src/lcd/dogm/u8g/HAL_LCD_class_defines.h
@@ -23,7 +23,7 @@
#include "../../../inc/MarlinConfig.h"
-// use this file to create the public interface for device drivers that are NOT in the U8G library
+// Use this file to create the public interface for device drivers that are NOT in the U8G library
extern u8g_dev_t u8g_dev_st7565_64128n_HAL_2x_sw_spi;
extern u8g_dev_t u8g_dev_st7565_64128n_HAL_2x_hw_spi;
@@ -90,6 +90,30 @@ class U8GLIB_SSD1306_128X64_2X_I2C_2_WIRE : public U8GLIB {
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)
+ extern u8g_dev_t u8g_dev_sh1106_128x64_HAL_sw_spi;
+ class U8GLIB_SH1106_128X64_SW_SPI_HAL : public U8GLIB {
+ public:
+ U8GLIB_SH1106_128X64_SW_SPI_HAL() : U8GLIB() { }
+ U8GLIB_SH1106_128X64_SW_SPI_HAL(pin_t sck, pin_t mosi, pin_t cs, pin_t reset = U8G_PIN_NONE) {init(sck, mosi, cs, reset); }
+ void init(pin_t sck, pin_t mosi, pin_t cs, pin_t reset=U8G_PIN_NONE) {
+ U8GLIB::init(&u8g_dev_sh1106_128x64_HAL_sw_spi, (uint8_t)sck, (uint8_t)mosi, (uint8_t)cs, U8G_PIN_NONE, (uint8_t)reset);
+ }
+ };
+#endif
+
+#if ENABLED(U8GLIB_SSD1306_SPI)
+ extern u8g_dev_t u8g_dev_ssd1306_128x64_HAL_sw_spi;
+ class U8GLIB_SSD1306_128X64_SW_SPI_HAL : public U8GLIB {
+ public:
+ U8GLIB_SSD1306_128X64_SW_SPI_HAL() : U8GLIB() { }
+ U8GLIB_SSD1306_128X64_SW_SPI_HAL(pin_t sck, pin_t mosi, pin_t cs, pin_t reset = U8G_PIN_NONE) {init(sck, mosi, cs, reset); }
+ void init(pin_t sck, pin_t mosi, pin_t cs, pin_t reset=U8G_PIN_NONE) {
+ U8GLIB::init(&u8g_dev_ssd1306_128x64_HAL_sw_spi, (uint8_t)sck, (uint8_t)mosi, (uint8_t)cs, U8G_PIN_NONE, (uint8_t)reset);
+ }
+ };
+#endif
+
//
// Very basic support for 320x240 TFT screen
// Tested on MKS Robin TFT_V2.0 with ST7789V controller
diff --git a/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1306_sh1106_128x64_I2C.cpp b/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1306_sh1106_128x64_I2C.cpp
index 4cd9b8f3c06d..4d4756c298cf 100644
--- a/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1306_sh1106_128x64_I2C.cpp
+++ b/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1306_sh1106_128x64_I2C.cpp
@@ -77,35 +77,34 @@
uint8_t u8g_WriteEscSeqP_2_wire(u8g_t *u8g, u8g_dev_t *dev, const uint8_t *esc_seq);
-// The sh1106 is compatible to the ssd1306, but is 132x64. 128x64 display area is centered within
-// the 132x64.
+// SH1106 (132x64) is compatible with SSD1306 (128x64) by adding a small margin to the larger display
+
+#define SH1106_PAGE_ADR(N) (0x20), (N)
+#define SH1106_COL_ADR(N) (0x10 | ((N) >> 4)), ((N) & 0xFF)
+#define SH1106_COLUMN_RANGE(N,O) (0x21), (N), (O)
+#define SH1106_PAGE_RANGE(N,O) (0x22), (N), (O)
+#define SH1106_SCROLL(N) ((N) ? 0x2F : 0x2E)
+#define SH1106_START_LINE(N) (0x40 | (N))
+#define SH1106_CONTRAST(N) (0x81), (N)
+#define SH1106_CHARGE_PUMP(N) (0x8D), ((N) ? 0x14 : 0x10)
+#define SH1106_ADC_REVERSE(N) ((N) ? 0xA1 : 0xA0)
+#define SH1106_ALL_PIX(N) ((N) ? 0xA5 : 0xA4)
+#define SH1106_INVERTED(N) ((N) ? 0xA7 : 0xA6)
+#define SH1106_MUX_RATIO(N) (0xA8), (N)
+#define SH1106_ON(N) ((N) ? 0xAF : 0xAE)
+#define SH1106_OUT_MODE(N) ((N) ? 0xC8 : 0xC0)
+#define SH1106_DISP_OFFS(N) (0xD3), (N)
+#define SH1106_OSC_FREQ(R,F) (0xD5), ((F) << 4 | (R))
+#define SH1106_CHARGE_PER(P,D) (0xD9), ((D) << 4 | (P))
+#define SH1106_COM_CONFIG(N) (0xDA), ((N) ? 0x12 : 0x02)
+#define SH1106_VCOM_DESEL(N) (0xDB), (N)
+#define SH1106_NOOP() (0xE3)
static const uint8_t u8g_dev_sh1106_128x64_data_start_2_wire[] PROGMEM = {
- 0x010, // set upper 4 bit of the col adr to 0
- 0x002, // set lower 4 bit of the col adr to 2 (centered display with ssd1306)
- U8G_ESC_END // end of sequence
+ SH1106_COL_ADR(2), // Column 2 to center 128 pixels in 132 pixels
+ U8G_ESC_END // End of sequence
};
-#define SH1106_PAGE_ADR(N) (0x20), (N)
-#define SH1106_COLUMN_RANGE(N) (0x21), (((N) >> 8) & 0xFF), ((N) & 0xFF)
-#define SH1106_PAGE_RANGE(N,O) (0x22), (N), (O)
-#define SH1106_SCROLL(N) ((N) ? 0x2F : 0x2E)
-#define SH1106_START_LINE(N) (0x40 | (N))
-#define SH1106_CONTRAST(N) (0x81), (N)
-#define SH1106_CHARGE_PUMP(N) (0x8D), ((N) ? 0x14 : 0x10)
-#define SH1106_ADC_REVERSE(N) ((N) ? 0xA1 : 0xA0)
-#define SH1106_ALL_PIX(N) ((N) ? 0xA5 : 0xA4)
-#define SH1106_INVERTED(N) ((N) ? 0xA7 : 0xA6)
-#define SH1106_MUX_RATIO(N) (0xA8), (N)
-#define SH1106_ON(N) ((N) ? 0xAF : 0xAE)
-#define SH1106_OUT_MODE(N) ((N) ? 0xC8 : 0xC0)
-#define SH1106_DISP_OFFS(N) (0xD3), (N)
-#define SH1106_OSC_FREQ(R,F) (0xD5), ((F) << 4 | (R))
-#define SH1106_CHARGE_PER(P,D) (0xD9), ((D) << 4 | (P))
-#define SH1106_COM_CONFIG(N) (0xDA), ((N) ? 0x12 : 0x02)
-#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
SH1106_ON(0), // Display off, sleep mode
@@ -113,19 +112,19 @@ static const uint8_t u8g_dev_sh1106_128x64_init_seq_2_wire[] PROGMEM = {
SH1106_DISP_OFFS(0), // Display offset
SH1106_START_LINE(0), // Start line
SH1106_ADC_REVERSE(1), // Segment remap A0/A1
- SH1106_OUT_MODE(1), // C0: scan dir normal, C8: reverse
- SH1106_COM_CONFIG(1), // Com pin HW config, sequential com pin config (bit 4), disable left/right remap (bit 5)
- SH1106_CONTRAST(0xCF), // [2] set contrast control
- SH1106_PAGE_ADR(0x02), // 2012-05-27: page addressing mode
- SH1106_COLUMN_RANGE(0x281), // Set column range from 0 through 131
- SH1106_PAGE_RANGE(0, 7), // Set page range from 0 through 7
- SH1106_CHARGE_PER(0x1, 0xF), // [2] pre-charge period 0x22/F1
+ SH1106_OUT_MODE(1), // 0: scan dir normal, 1: reverse
+ SH1106_COM_CONFIG(1), // COM pin HW config, sequential com pin config (bit 4), disable left/right remap (bit 5)
+ SH1106_CONTRAST(0xCF), // Set contrast control
+ SH1106_PAGE_ADR(0x02), // page addressing mode
+ SH1106_COLUMN_RANGE(2, 129), // Set column range 2 .. 129
+ SH1106_PAGE_RANGE(0, 7), // Set page range 0 .. 7
+ SH1106_CHARGE_PER(0x1, 0xF), // Pre-charge period
SH1106_VCOM_DESEL(0x40), // Vcomh deselect level
- SH1106_ALL_PIX(0), // Output ram to display
+ SH1106_ALL_PIX(0), // Output RAM to display
SH1106_INVERTED(0), // Normal display mode
SH1106_OSC_FREQ(0, 8), // Clock divide ratio (0:1) and oscillator frequency (8)
- SH1106_CHARGE_PUMP(1), // [2] charge pump setting (P62): 0x14 enable, 0x10 disable
- SH1106_SCROLL(0), // 2012-05-27: Deactivate scroll
+ SH1106_CHARGE_PUMP(1), // Charge pump setting
+ SH1106_SCROLL(0), // Deactivate scroll
SH1106_ON(1), // Display on
U8G_ESC_END // End of sequence
};
@@ -136,28 +135,24 @@ uint8_t u8g_dev_sh1106_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t m
u8g_InitCom(u8g, dev, U8G_SPI_CLK_CYCLE_300NS);
u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_sh1106_128x64_init_seq_2_wire);
break;
- case U8G_DEV_MSG_STOP:
- break;
+ case U8G_DEV_MSG_STOP: break;
case U8G_DEV_MSG_PAGE_NEXT: {
- u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem);
- u8g_SetAddress(u8g, dev, 0); // instruction mode
- u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_sh1106_128x64_data_start_2_wire);
- u8g_WriteByte(u8g, dev, 0x0B0 | (pb->p.page*2)); // select current page
- u8g_SetAddress(u8g, dev, 1); // data mode
- u8g_WriteSequence(u8g, dev, pb->width, (uint8_t *) pb->buf);
- u8g_SetChipSelect(u8g, dev, 0);
- u8g_SetAddress(u8g, dev, 0); // instruction mode
- u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_sh1106_128x64_data_start_2_wire);
- u8g_WriteByte(u8g, dev, 0x0B0 | (pb->p.page*2+1)); // select current page
- u8g_SetAddress(u8g, dev, 1); // data mode
- 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_OFF:
- return 1;
+ u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem);
+ u8g_SetAddress(u8g, dev, 0); // Instruction mode
+ u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_sh1106_128x64_data_start_2_wire);
+ u8g_WriteByte(u8g, dev, 0xB0 | (pb->p.page*2)); // Select current page
+ u8g_SetAddress(u8g, dev, 1); // Data mode
+ u8g_WriteSequence(u8g, dev, pb->width, (uint8_t *) pb->buf);
+ u8g_SetChipSelect(u8g, dev, 0);
+ u8g_SetAddress(u8g, dev, 0); // Instruction mode
+ u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_sh1106_128x64_data_start_2_wire);
+ u8g_WriteByte(u8g, dev, 0xB0 | (pb->p.page*2+1)); // Select current page
+ u8g_SetAddress(u8g, dev, 1); // Data mode
+ 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_OFF: return 1;
}
return u8g_dev_pb16v1_base_fn(u8g, dev, msg, arg);
}
@@ -169,33 +164,32 @@ u8g_dev_t u8g_dev_sh1106_128x64_2x_i2c_2_wire = { u8g_dev_sh1106_128x64_2x_2_wir
/////////////////////////////////////////////////////////////////////////////////////////////
static const uint8_t u8g_dev_ssd1306_128x64_data_start_2_wire[] PROGMEM = {
- 0x010, // set upper 4 bit of the col adr to 0
- 0x000, // set lower 4 bit of the col adr to 0
- U8G_ESC_END // end of sequence
+ SH1106_COL_ADR(0), // Column 0
+ 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
- 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
- 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
- 0x0D9, 0x0F1, // [2] pre-charge period 0x022/f1
- 0x0DB, 0x040, // vcomh deselect level
- 0x0A4, // output ram to display
- 0x0A6, // none inverted normal display mode
- 0x0D5, 0x080, // clock divide ratio (0x00=1) and oscillator frequency (0x8)
- 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
+ U8G_ESC_CS(0), // Disable chip
+ SH1106_ON(0), // Display off, sleep mode
+ SH1106_MUX_RATIO(0x3F), // Mux ratio
+ SH1106_DISP_OFFS(0), // Display offset
+ SH1106_START_LINE(0), // Start line
+ SH1106_ADC_REVERSE(1), // Segment remap A0/A1
+ SH1106_OUT_MODE(1), // 0: scan dir normal, 1: reverse
+ SH1106_COM_CONFIG(1), // COM pin HW config, sequential com pin config (bit 4), disable left/right remap (bit 5)
+ SH1106_CONTRAST(0xCF), // Set contrast control
+ SH1106_PAGE_ADR(0x02), // page addressing mode
+ SH1106_COLUMN_RANGE(0, 127), // Set column range 0 .. 127
+ SH1106_PAGE_RANGE(0, 7), // Set page range from 0 .. 7
+ SH1106_CHARGE_PER(0x1, 0xF), // Pre-charge period
+ SH1106_VCOM_DESEL(0x40), // Vcomh deselect level
+ SH1106_ALL_PIX(0), // Send RAM to display
+ SH1106_INVERTED(0), // Normal display mode
+ SH1106_OSC_FREQ(0, 8), // Clock divide ratio (0:1) and oscillator frequency (8)
+ SH1106_CHARGE_PUMP(1), // Charge pump setting
+ SH1106_SCROLL(0), // Deactivate scroll
+ SH1106_ON(1), // Display on
+ U8G_ESC_END // End of sequence
};
uint8_t u8g_dev_ssd1306_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg) {
@@ -204,28 +198,24 @@ uint8_t u8g_dev_ssd1306_128x64_2x_2_wire_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t
u8g_InitCom(u8g, dev, U8G_SPI_CLK_CYCLE_300NS);
u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_ssd1306_128x64_init_seq_2_wire);
break;
- case U8G_DEV_MSG_STOP:
- break;
+ case U8G_DEV_MSG_STOP: break;
case U8G_DEV_MSG_PAGE_NEXT: {
- u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem);
- u8g_SetAddress(u8g, dev, 0); // instruction mode
- u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_ssd1306_128x64_data_start_2_wire);
- u8g_WriteByte(u8g, dev, 0x0B0 | (pb->p.page*2)); // select current page
- u8g_SetAddress(u8g, dev, 1); // data mode
- u8g_WriteSequence(u8g, dev, pb->width, (uint8_t *) pb->buf);
- u8g_SetChipSelect(u8g, dev, 0);
- u8g_SetAddress(u8g, dev, 0); // instruction mode
- u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_ssd1306_128x64_data_start_2_wire);
- u8g_WriteByte(u8g, dev, 0x0B0 | (pb->p.page*2+1)); // select current page
- u8g_SetAddress(u8g, dev, 1); // data mode
- 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_OFF:
- return 1;
+ u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem);
+ u8g_SetAddress(u8g, dev, 0); // Instruction mode
+ u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_ssd1306_128x64_data_start_2_wire);
+ u8g_WriteByte(u8g, dev, 0xB0 | (pb->p.page*2)); // Select current page
+ u8g_SetAddress(u8g, dev, 1); // Data mode
+ u8g_WriteSequence(u8g, dev, pb->width, (uint8_t *) pb->buf);
+ u8g_SetChipSelect(u8g, dev, 0);
+ u8g_SetAddress(u8g, dev, 0); // Instruction mode
+ u8g_WriteEscSeqP_2_wire(u8g, dev, u8g_dev_ssd1306_128x64_data_start_2_wire);
+ u8g_WriteByte(u8g, dev, 0xB0 | (pb->p.page*2+1)); // Select current page
+ u8g_SetAddress(u8g, dev, 1); // Data mode
+ 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_OFF: return 1;
}
return u8g_dev_pb16v1_base_fn(u8g, dev, msg, arg);
}
@@ -236,10 +226,10 @@ u8g_dev_t u8g_dev_ssd1306_128x64_2x_i2c_2_wire = { u8g_dev_ssd1306_128x64_2x_2_w
/////////////////////////////////////////////////////////////////////////////////////////////
-// This routine adds the instruction byte in between the command bytes. This makes the init
-// sequences a lot easier to read.
+// This routine adds the instruction byte in between the command bytes.
+// This makes the init sequences a lot easier to read.
-#define I2C_CMD_MODE 0x080
+#define I2C_CMD_MODE 0x80
uint8_t u8g_WriteEscSeqP_2_wire(u8g_t *u8g, u8g_dev_t *dev, const uint8_t *esc_seq) {
uint8_t is_escape = 0;
@@ -247,10 +237,8 @@ uint8_t u8g_WriteEscSeqP_2_wire(u8g_t *u8g, u8g_dev_t *dev, const uint8_t *esc_s
uint8_t value = u8g_pgm_read(esc_seq);
if (is_escape == 0) {
if (value != 255) {
- if (u8g_WriteByte(u8g, dev, value) == 0 )
- return 0;
- if (u8g_WriteByte(u8g, dev, I2C_CMD_MODE) == 0 )
- return 0;
+ if (u8g_WriteByte(u8g, dev, value) == 0) return 0;
+ if (u8g_WriteByte(u8g, dev, I2C_CMD_MODE) == 0) return 0;
}
else {
is_escape = 1;
@@ -258,16 +246,14 @@ uint8_t u8g_WriteEscSeqP_2_wire(u8g_t *u8g, u8g_dev_t *dev, const uint8_t *esc_s
}
else {
if (value == 255) {
- if (u8g_WriteByte(u8g, dev, value) == 0 )
- return 0;
- if (u8g_WriteByte(u8g, dev, I2C_CMD_MODE) == 0 )
- return 0;
+ if (u8g_WriteByte(u8g, dev, value) == 0) return 0;
+ if (u8g_WriteByte(u8g, dev, I2C_CMD_MODE) == 0) return 0;
}
else if (value == 254) {
break;
}
- else if (value >= 0x0F0) {
- /* not yet used, do nothing */
+ else if (value >= 0xF0) {
+ // not yet used, do nothing
}
else if (value >= 0xE0 ) {
u8g_SetAddress(u8g, dev, value & 0x0F);
@@ -279,13 +265,14 @@ uint8_t u8g_WriteEscSeqP_2_wire(u8g_t *u8g, u8g_dev_t *dev, const uint8_t *esc_s
u8g_SetResetLow(u8g, dev);
value &= 0x0F;
value <<= 4;
- value+=2;
+ value += 2;
u8g_Delay(value);
u8g_SetResetHigh(u8g, dev);
u8g_Delay(value);
}
- else if (value >= 0xBE) { /* not yet implemented */
- /* u8g_SetVCC(u8g, dev, value & 0x01); */
+ else if (value >= 0xBE) {
+ // not yet implemented
+ //u8g_SetVCC(u8g, dev, value & 0x01);
}
else if (value <= 127) {
u8g_Delay(value);
diff --git a/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1306_sh1106_128x64_SWSPI.cpp b/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1306_sh1106_128x64_SWSPI.cpp
new file mode 100644
index 000000000000..afb3c28a8ce3
--- /dev/null
+++ b/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1306_sh1106_128x64_SWSPI.cpp
@@ -0,0 +1,246 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 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 .
+ *
+ */
+
+/**
+ * Based on u8g_dev_ssd1306_128x64.c
+ *
+ * Universal 8bit Graphics Library
+ *
+ * Copyright (c) 2015, olikraus@gmail.com
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if ALL(HAS_MARLINUI_U8GLIB, FORCE_SOFT_SPI) && ANY(U8GLIB_SH1106_SPI, U8GLIB_SSD1306_SPI)
+
+#include "HAL_LCD_com_defines.h"
+
+#define WIDTH 128
+#define HEIGHT 64
+#define PAGE_HEIGHT 8
+
+#define SH1106_PAGE_ADR(N) (0x20), (N)
+#define SH1106_COL_ADR(N) (0x10 | ((N) >> 4)), ((N) & 0xFF)
+#define SH1106_COLUMN_RANGE(N,O) (0x21), (N), (O)
+#define SH1106_PAGE_RANGE(N,O) (0x22), (N), (O)
+#define SH1106_SCROLL(N) ((N) ? 0x2F : 0x2E)
+#define SH1106_START_LINE(N) (0x40 | (N))
+#define SH1106_CONTRAST(N) (0x81), (N)
+#define SH1106_CHARGE_PUMP(N) (0x8D), ((N) ? 0x14 : 0x10)
+#define SH1106_ADC_REVERSE(N) ((N) ? 0xA1 : 0xA0)
+#define SH1106_ALL_PIX(N) ((N) ? 0xA5 : 0xA4)
+#define SH1106_INVERTED(N) ((N) ? 0xA7 : 0xA6)
+#define SH1106_MUX_RATIO(N) (0xA8), (N)
+#define SH1106_ON(N) ((N) ? 0xAF : 0xAE)
+#define SH1106_OUT_MODE(N) ((N) ? 0xC8 : 0xC0)
+#define SH1106_DISP_OFFS(N) (0xD3), (N)
+#define SH1106_OSC_FREQ(R,F) (0xD5), ((F) << 4 | (R))
+#define SH1106_CHARGE_PER(P,D) (0xD9), ((D) << 4 | (P))
+#define SH1106_COM_CONFIG(N) (0xDA), ((N) ? 0x12 : 0x02)
+#define SH1106_VCOM_DESEL(N) (0xDB), (N)
+#define SH1106_NOOP() (0xE3)
+
+static const uint8_t u8g_dev_ssd13xx_HAL_sleep_on[] PROGMEM = {
+ U8G_ESC_ADR(0), // Instruction mode
+ U8G_ESC_CS(1), // Enable chip
+ SH1106_ON(0) // Display off
+ U8G_ESC_CS(0), // Disable chip
+ U8G_ESC_END // End of sequence
+};
+
+static const uint8_t u8g_dev_ssd13xx_HAL_sleep_off[] PROGMEM = {
+ U8G_ESC_ADR(0), // Instruction mode
+ U8G_ESC_CS(1), // Enable chip
+ SH1106_ON(1), // Display on
+ U8G_ESC_DLY(50), // Delay 50 ms
+ U8G_ESC_CS(0), // Disable chip
+ U8G_ESC_END // End of sequence
+};
+
+#if ENABLED(U8GLIB_SH1106_SPI)
+
+// Init sequence Adafruit 128x64 OLED (NOT TESTED). Like Adafruit3, but with page addressing mode.
+static const uint8_t u8g_dev_sh1106_128x64_HAL_init_seq[] PROGMEM = {
+ U8G_ESC_CS(0), // Disable chip
+ U8G_ESC_ADR(0), // Instruction mode
+ U8G_ESC_RST(1), // Do reset low pulse with (1*16)+2 milliseconds
+ U8G_ESC_CS(1), // Enable chip
+ SH1106_ON(0), // Display off, sleep mode
+ SH1106_OSC_FREQ(0, 8), // Clock divide ratio (0:1) and oscillator frequency (8)
+ SH1106_MUX_RATIO(0x3F), // Mux ratio
+ SH1106_DISP_OFFS(0), // Display offset
+ SH1106_START_LINE(0), // Start line
+ SH1106_CHARGE_PUMP(1), // Charge pump setting
+ SH1106_PAGE_ADR(0x02), // page addressing mode
+ SH1106_ADC_REVERSE(1), // Segment remap A0/A1
+ SH1106_OUT_MODE(1), // 0: scan dir normal, 1: reverse
+ SH1106_COM_CONFIG(1), // COM pin HW config, sequential com pin config (bit 4), disable left/right remap (bit 5)
+ SH1106_CONTRAST(0x80), // Set contrast control
+ SH1106_CHARGE_PER(0x1, 0xF), // Pre-charge period
+ SH1106_VCOM_DESEL(0x40), // Vcomh deselect level
+ SH1106_SCROLL(0), // Deactivate scroll
+ SH1106_ALL_PIX(0), // Output RAM to display
+ SH1106_INVERTED(0), // Normal display mode
+ SH1106_ON(1), // Display on
+ U8G_ESC_CS(0), // Disable chip
+ U8G_ESC_END // End of sequence
+};
+
+// SH1106 (132x64) is compatible with SSD1306 (128x64) by adding a small margin to the larger display
+static const uint8_t u8g_dev_sh1106_128x64_HAL_data_start[] PROGMEM = {
+ U8G_ESC_ADR(0), // Instruction mode
+ U8G_ESC_CS(1), // Enable chip
+ SH1106_COL_ADR(2), // Column 2 to center 128 pixels in 132 pixels
+ U8G_ESC_END // End of sequence
+};
+
+uint8_t u8g_dev_sh1106_128x64_HAL_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(u8g, dev, u8g_dev_sh1106_128x64_HAL_init_seq);
+ break;
+ case U8G_DEV_MSG_STOP:
+ break;
+ case U8G_DEV_MSG_PAGE_NEXT: {
+ u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem);
+ u8g_WriteEscSeqP(u8g, dev, u8g_dev_sh1106_128x64_HAL_data_start);
+ u8g_WriteByte(u8g, dev, 0x0B0 | pb->p.page); // Select current page (SSD1306)
+ u8g_SetAddress(u8g, dev, 1); // Data mode
+ if (u8g_pb_WriteBuffer(pb, u8g, dev) == 0) return 0;
+ u8g_SetChipSelect(u8g, dev, 0);
+ } break;
+ case U8G_DEV_MSG_SLEEP_ON:
+ u8g_WriteEscSeqP(u8g, dev, u8g_dev_ssd13xx_HAL_sleep_on);
+ return 1;
+ case U8G_DEV_MSG_SLEEP_OFF:
+ u8g_WriteEscSeqP(u8g, dev, u8g_dev_ssd13xx_HAL_sleep_off);
+ return 1;
+ case U8G_DEV_MSG_CONTRAST:
+ u8g_SetChipSelect(u8g, dev, 1);
+ u8g_SetAddress(u8g, dev, 0); // Instruction mode
+ u8g_WriteByte(u8g, dev, 0x81);
+ u8g_WriteByte(u8g, dev, *(uint8_t *) arg);
+ u8g_SetChipSelect(u8g, dev, 0);
+ return 1;
+ }
+ return u8g_dev_pb8v1_base_fn(u8g, dev, msg, arg);
+}
+
+U8G_PB_DEV(u8g_dev_sh1106_128x64_HAL_sw_spi, WIDTH, HEIGHT, PAGE_HEIGHT, u8g_dev_sh1106_128x64_HAL_fn, U8G_COM_HAL_SW_SPI_FN);
+
+#elif ENABLED(U8GLIB_SSD1306_SPI)
+
+static const uint8_t u8g_dev_ssd1306_128x64_HAL_init_seq[] PROGMEM = {
+ U8G_ESC_CS(0), // Disable chip
+ U8G_ESC_ADR(0), // Instruction mode
+ U8G_ESC_RST(1), // Do reset low pulse with (1*16)+2 milliseconds
+ U8G_ESC_CS(1), // Enable chip
+ SH1106_ON(0), // Display off, sleep mode
+ SH1106_OSC_FREQ(0, 8), // Clock divide ratio (0:1) and oscillator frequency (8)
+ SH1106_MUX_RATIO(0x3F), // Mux ratio
+ SH1106_DISP_OFFS(0), // Display offset
+ SH1106_START_LINE(0), // Start line
+ SH1106_CHARGE_PUMP(1), // Charge pump setting
+ SH1106_PAGE_ADR(0x02), // page addressing mode
+ SH1106_ADC_REVERSE(1), // Segment remap A0/A1
+ SH1106_OUT_MODE(1), // 0: scan dir normal, 1: reverse
+ SH1106_COM_CONFIG(1), // COM pin HW config, sequential com pin config (bit 4), disable left/right remap (bit 5)
+ SH1106_CONTRAST(0x80), // Set contrast control
+ SH1106_CHARGE_PER(0x1, 0xF), // Pre-charge period
+ SH1106_VCOM_DESEL(0x40), // Vcomh deselect level
+ SH1106_SCROLL(0), // Deactivate scroll
+ SH1106_ALL_PIX(0), // Output RAM to display
+ SH1106_INVERTED(0), // Normal display mode
+ SH1106_ON(1), // Display on
+ U8G_ESC_CS(0), // Disable chip
+ U8G_ESC_END // End of sequence
+};
+
+static const uint8_t u8g_dev_ssd1306_128x64_HAL_data_start[] PROGMEM = {
+ U8G_ESC_ADR(0), // Instruction mode
+ U8G_ESC_CS(1), // Enable chip
+ SH1106_COL_ADR(0), // Column 0
+ U8G_ESC_END // End of sequence
+};
+
+uint8_t u8g_dev_ssd1306_128x64_HAL_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_400NS);
+ u8g_WriteEscSeqP(u8g, dev, u8g_dev_ssd1306_128x64_HAL_init_seq);
+ break;
+ case U8G_DEV_MSG_STOP: break;
+ case U8G_DEV_MSG_PAGE_NEXT: {
+ u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem);
+ u8g_WriteEscSeqP(u8g, dev, u8g_dev_ssd1306_128x64_HAL_data_start);
+ u8g_WriteByte(u8g, dev, 0x0b0 | pb->p.page); // Select current page (SSD1306)
+ u8g_SetAddress(u8g, dev, 1); // Data mode
+ if (u8g_pb_WriteBuffer(pb, u8g, dev) == 0) return 0;
+ u8g_SetChipSelect(u8g, dev, 0);
+ } break;
+ case U8G_DEV_MSG_SLEEP_ON:
+ u8g_WriteEscSeqP(u8g, dev, u8g_dev_ssd13xx_HAL_sleep_on);
+ return 1;
+ case U8G_DEV_MSG_SLEEP_OFF:
+ u8g_WriteEscSeqP(u8g, dev, u8g_dev_ssd13xx_HAL_sleep_off);
+ return 1;
+ case U8G_DEV_MSG_CONTRAST:
+ u8g_SetChipSelect(u8g, dev, 1);
+ u8g_SetAddress(u8g, dev, 0); // Instruction mode
+ u8g_WriteByte(u8g, dev, 0x81);
+ u8g_WriteByte(u8g, dev, *(uint8_t *) arg);
+ u8g_SetChipSelect(u8g, dev, 0);
+ return 1;
+ }
+ return u8g_dev_pb8v1_base_fn(u8g, dev, msg, arg);
+}
+
+U8G_PB_DEV(u8g_dev_ssd1306_128x64_HAL_sw_spi, WIDTH, HEIGHT, PAGE_HEIGHT, u8g_dev_ssd1306_128x64_HAL_fn, U8G_COM_HAL_SW_SPI_FN);
+
+#endif // U8GLIB_SSD1306_SPI
+#endif // HAS_MARLINUI_U8GLIB
diff --git a/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1309_12864.cpp b/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1309_12864.cpp
index 4aa90d5e8e82..010231d0561f 100644
--- a/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1309_12864.cpp
+++ b/Marlin/src/lcd/dogm/u8g/u8g_dev_ssd1309_12864.cpp
@@ -31,61 +31,83 @@
#define HEIGHT 64
#define PAGE_HEIGHT 8
+#define SSD1309_PAGE_ADR(N) (0x20), (N)
+#define SSD1309_COL_ADR(N) (0x10 | ((N) >> 4)), ((N) & 0xFF)
+#define SSD1309_COLUMN_RANGE(N,O) (0x21), (N), (O)
+#define SSD1309_PAGE_RANGE(N,O) (0x22), (N), (O)
+#define SSD1309_SCROLL(N) ((N) ? 0x2F : 0x2E)
+#define SSD1309_START_LINE(N) (0x40 | (N))
+#define SSD1309_CONTRAST(N) (0x81), (N)
+#define SSD1309_CHARGE_PUMP(N) (0x8D), ((N) ? 0x14 : 0x10)
+#define SSD1309_ADC_REVERSE(N) ((N) ? 0xA1 : 0xA0)
+#define SSD1309_ALL_PIX(N) ((N) ? 0xA5 : 0xA4)
+#define SSD1309_INVERTED(N) ((N) ? 0xA7 : 0xA6)
+#define SSD1309_MUX_RATIO(N) (0xA8), (N)
+#define SSD1309_ON(N) ((N) ? 0xAF : 0xAE)
+#define SSD1309_OUT_MODE(N) ((N) ? 0xC8 : 0xC0)
+#define SSD1309_DISP_OFFS(N) (0xD3), (N)
+#define SSD1309_OSC_FREQ(R,F) (0xD5), ((F) << 4 | (R))
+#define SSD1309_CHARGE_PER(P,D) (0xD9), ((D) << 4 | (P))
+#define SSD1309_COM_CONFIG(N) (0xDA), ((N) ? 0x12 : 0x02)
+#define SSD1309_VCOM_DESEL(N) (0xDB), (N)
+#define SSD1309_NOOP() (0xE3)
+#define SSD1309_COMMAND_LOCK(N) (0xFD), ((N) ? 0x16 : 0x12)
+
// SSD1309 init sequence
static const uint8_t u8g_dev_ssd1309_128x64_init_seq[] PROGMEM = {
- U8G_ESC_CS(0), // Disable chip
- U8G_ESC_ADR(0), // Instruction mode
- U8G_ESC_RST(1), // Do reset low pulse with (1*16)+2 milliseconds
- U8G_ESC_CS(1), // Enable chip
+ U8G_ESC_CS(0), // Disable chip
+ U8G_ESC_ADR(0), // Instruction mode
+ U8G_ESC_RST(1), // Do reset low pulse with (1*16)+2 milliseconds
+ U8G_ESC_CS(1), // Enable chip
+
+ SSD1309_COMMAND_LOCK(0), // Unlock OLED driver IC MCU command interface
+ SSD1309_ON(0),
+ SSD1309_OSC_FREQ(0, 10), // Clock divide ratio (0:1) and oscillator frequency (8)
+ SSD1309_MUX_RATIO(0x3F), // Mux ratio
+ SSD1309_DISP_OFFS(0), // Display offset
+ SSD1309_START_LINE(0), // Start line
+ SSD1309_ADC_REVERSE(1), // Segment remap A0/A1
+ SSD1309_OUT_MODE(1), // 0: scan dir normal, 1: reverse
+ SSD1309_COM_CONFIG(1), // COM pin HW config, sequential com pin config (bit 4), disable left/right remap (bit 5)
+ SSD1309_CONTRAST(0xDF), // Set contrast control
+ SSD1309_CHARGE_PER(0x2, 0x8), // Pre-charge period
+ SSD1309_VCOM_DESEL(0x34), // Vcomh deselect level
+ SSD1309_ALL_PIX(0), // Output RAM to display
+ SSD1309_INVERTED(0), // Normal display mode
- 0xFD,0x12, // Command Lock
- 0xAE, // Set Display Off
- 0xD5,0xA0, // Set Display Clock Divide Ratio/Oscillator Frequency
- 0xA8,0x3F, // Set Multiplex Ratio
- 0x3D,0x00, // Set Display Offset
- 0x40, // Set Display Start Line
- 0xA1, // Set Segment Re-Map
- 0xC8, // Set COM Output Scan Direction
- 0xDA,0x12, // Set COM Pins Hardware Configuration
- 0x81,0xDF, // Set Current Control
- 0xD9,0x82, // Set Pre-Charge Period
- 0xDB,0x34, // Set VCOMH Deselect Level
- 0xA4, // Set Entire Display On/Off
- 0xA6, // Set Normal/Inverse Display
- U8G_ESC_VCC(1), // Power up VCC & Stabilized
+ U8G_ESC_VCC(1), // Power up VCC & stabilize
U8G_ESC_DLY(50),
- 0xAF, // Set Display On
+ SSD1309_ON(1), // Display on
U8G_ESC_DLY(50),
- U8G_ESC_CS(0), // Disable chip
- U8G_ESC_END // End of sequence
+ U8G_ESC_CS(0), // Disable chip
+ U8G_ESC_END // End of sequence
};
// Select one init sequence here
#define u8g_dev_ssd1309_128x64_init_seq u8g_dev_ssd1309_128x64_init_seq
static const uint8_t u8g_dev_ssd1309_128x64_data_start[] PROGMEM = {
- U8G_ESC_ADR(0), // Instruction mode
- U8G_ESC_CS(1), // Enable chip
- 0x010, // Set upper 4 bit of the col adr to 0
- 0x000, // Set lower 4 bit of the col adr to 4
- U8G_ESC_END // End of sequence
+ U8G_ESC_ADR(0), // Instruction mode
+ U8G_ESC_CS(1), // Enable chip
+ SSD1309_COL_ADR(0), // Column 0
+ U8G_ESC_END // End of sequence
};
static const uint8_t u8g_dev_ssd13xx_sleep_on[] PROGMEM = {
- U8G_ESC_ADR(0), // Instruction mode
- U8G_ESC_CS(1), // Enable chip
- 0x0AE, // Display off
- U8G_ESC_CS(0), // Disable chip
- U8G_ESC_END // End of sequence
+ U8G_ESC_ADR(0), // Instruction mode
+ U8G_ESC_CS(1), // Enable chip
+ SSD1309_ON(0), // Display off
+ U8G_ESC_CS(0), // Disable chip
+ U8G_ESC_END // End of sequence
};
static const uint8_t u8g_dev_ssd13xx_sleep_off[] PROGMEM = {
- U8G_ESC_ADR(0), // Instruction mode
- U8G_ESC_CS(1), // Enable chip
- 0x0AF, // Display on
- U8G_ESC_DLY(50), // Delay 50 ms
- U8G_ESC_CS(0), // Disable chip
- U8G_ESC_END // End of sequence
+ U8G_ESC_ADR(0), // Instruction mode
+ U8G_ESC_CS(1), // Enable chip
+ SSD1309_ON(1), // Display on
+ U8G_ESC_DLY(50), // Delay 50 ms
+ U8G_ESC_CS(0), // Disable chip
+ U8G_ESC_END // End of sequence
};
uint8_t u8g_dev_ssd1309_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg) {
@@ -99,7 +121,7 @@ uint8_t u8g_dev_ssd1309_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void
case U8G_DEV_MSG_PAGE_NEXT: {
u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem);
u8g_WriteEscSeqP(u8g, dev, u8g_dev_ssd1309_128x64_data_start);
- u8g_WriteByte(u8g, dev, 0x0B0 | pb->p.page); // Select current page (SSD1306)
+ u8g_WriteByte(u8g, dev, 0xB0 | pb->p.page); // Select current page (SSD1306)
u8g_SetAddress(u8g, dev, 1); // Data mode
if (u8g_pb_WriteBuffer(pb, u8g, dev) == 0) return 0;
u8g_SetChipSelect(u8g, dev, 0);
@@ -108,8 +130,8 @@ uint8_t u8g_dev_ssd1309_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void
case U8G_DEV_MSG_CONTRAST:
u8g_SetChipSelect(u8g, dev, 1);
u8g_SetAddress(u8g, dev, 0); // Instruction mode
- u8g_WriteByte(u8g, dev, 0x081);
- u8g_WriteByte(u8g, dev, (*(uint8_t *)arg) ); // 11 Jul 2015: fixed contrast calculation
+ u8g_WriteByte(u8g, dev, 0x81);
+ u8g_WriteByte(u8g, dev, (*(uint8_t *)arg));
u8g_SetChipSelect(u8g, dev, 0);
return 1;
case U8G_DEV_MSG_SLEEP_ON:
diff --git a/Marlin/src/lcd/dogm/u8g/u8g_dev_uc1701_mini12864_HAL.cpp b/Marlin/src/lcd/dogm/u8g/u8g_dev_uc1701_mini12864_HAL.cpp
index 95ae2810f256..f9c68c15fc15 100644
--- a/Marlin/src/lcd/dogm/u8g/u8g_dev_uc1701_mini12864_HAL.cpp
+++ b/Marlin/src/lcd/dogm/u8g/u8g_dev_uc1701_mini12864_HAL.cpp
@@ -130,7 +130,7 @@ static const uint8_t u8g_dev_uc1701_mini12864_HAL_data_start[] PROGMEM = {
UC1701_V5_RATIO(3), // set V0 voltage resistor ratio to large
UC1701_INDICATOR(0), // indicator disable
UC1701_ON(1), // display on
- UC1701_COLUMN_HI(0), // set upper 4 bit of the col adr to 0
+ UC1701_COLUMN_HI(0), // set upper 4 bits of the col adr to 0
U8G_ESC_END, // end of sequence
U8G_ESC_DLY(5) // delay 5 ms
#else
diff --git a/Marlin/src/lcd/extui/dgus/mks/DGUSDisplayDef.cpp b/Marlin/src/lcd/extui/dgus/mks/DGUSDisplayDef.cpp
index d78364cb3bc1..8e2b1bb8e48a 100644
--- a/Marlin/src/lcd/extui/dgus/mks/DGUSDisplayDef.cpp
+++ b/Marlin/src/lcd/extui/dgus/mks/DGUSDisplayDef.cpp
@@ -69,7 +69,7 @@ void MKS_reset_settings() {
{ 20, 20 }, { 20, 20 },
{ X_CENTER, Y_CENTER }
};
- mks_language_index = MKS_SimpleChinese;
+ mks_language_index = MKS_English;
COPY(mks_corner_offsets, init_dgus_level_offsets);
mks_park_pos.set(20, 20, 10);
mks_min_extrusion_temp = 0;
diff --git a/Marlin/src/lcd/extui/dgus_e3s1pro/DGUSRxHandler.cpp b/Marlin/src/lcd/extui/dgus_e3s1pro/DGUSRxHandler.cpp
index dc80a7aed02b..118cfd35143b 100644
--- a/Marlin/src/lcd/extui/dgus_e3s1pro/DGUSRxHandler.cpp
+++ b/Marlin/src/lcd/extui/dgus_e3s1pro/DGUSRxHandler.cpp
@@ -101,7 +101,7 @@ void DGUSRxHandler::retractLength(DGUS_VP &vp, void *data) {
void DGUSRxHandler::setLanguage(DGUS_VP &vp, void *data) {
DGUS_Data::Language language = (DGUS_Data::Language)Endianness::fromBE_P(data);
- ui_language = screen.config.language = language;
+ screen.config.language = language;
screen.triggerEEPROMSave();
screen.triggerFullUpdate();
}
diff --git a/Marlin/src/lcd/extui/mks_ui/draw_printing.cpp b/Marlin/src/lcd/extui/mks_ui/draw_printing.cpp
index 345c5dd732c0..ee913d646548 100644
--- a/Marlin/src/lcd/extui/mks_ui/draw_printing.cpp
+++ b/Marlin/src/lcd/extui/mks_ui/draw_printing.cpp
@@ -294,7 +294,7 @@ void setProBarRate() {
lv_label_set_text(bar1ValueText, public_buf_l);
lv_obj_align(bar1ValueText, bar1, LV_ALIGN_CENTER, 0, 0);
- if (marlin_state == MF_SD_COMPLETE) {
+ if (marlin_state == MarlinState::MF_SD_COMPLETE) {
if (once_flag == 0) {
stop_print_time();
@@ -309,7 +309,7 @@ void setProBarRate() {
if (gCfgItems.finish_power_off) {
gcode.process_subcommands_now(F("M1001"));
queue.inject(F("M81"));
- marlin_state = MF_RUNNING;
+ marlin_state = MarlinState::MF_RUNNING;
}
#endif
}
diff --git a/Marlin/src/lcd/extui/mks_ui/draw_ui.cpp b/Marlin/src/lcd/extui/mks_ui/draw_ui.cpp
index 7408197f73fe..bd06e6c1f482 100644
--- a/Marlin/src/lcd/extui/mks_ui/draw_ui.cpp
+++ b/Marlin/src/lcd/extui/mks_ui/draw_ui.cpp
@@ -766,7 +766,7 @@ void GUI_RefreshPage() {
disp_print_time();
disp_fan_Zpos();
}
- if (printing_rate_update_flag || marlin_state == MF_SD_COMPLETE) {
+ if (printing_rate_update_flag || marlin_state == MarlinState::MF_SD_COMPLETE) {
printing_rate_update_flag = false;
if (!gcode_preview_over) setProBarRate();
}
diff --git a/Marlin/src/lcd/extui/ui_api.cpp b/Marlin/src/lcd/extui/ui_api.cpp
index 1fcfabdd7257..d89a09d8ada2 100644
--- a/Marlin/src/lcd/extui/ui_api.cpp
+++ b/Marlin/src/lcd/extui/ui_api.cpp
@@ -1219,7 +1219,7 @@ namespace ExtUI {
void onSurviveInKilled() {
thermalManager.disable_all_heaters();
flags.printer_killed = 0;
- marlin_state = MF_RUNNING;
+ marlin_state = MarlinState::MF_RUNNING;
//SERIAL_ECHOLNPGM("survived at: ", millis());
}
diff --git a/Marlin/src/lcd/marlinui.cpp b/Marlin/src/lcd/marlinui.cpp
index a967d921359f..8b24cd26eb46 100644
--- a/Marlin/src/lcd/marlinui.cpp
+++ b/Marlin/src/lcd/marlinui.cpp
@@ -25,7 +25,7 @@
#include "../MarlinCore.h" // for printingIsPaused
#include "../gcode/parser.h" // for axis_is_rotational, using_inch_units
-#if LED_POWEROFF_TIMEOUT > 0 || ALL(HAS_WIRED_LCD, PRINTER_EVENT_LEDS) || (HAS_BACKLIGHT_TIMEOUT && defined(NEOPIXEL_BKGD_INDEX_FIRST))
+#if HAS_LED_POWEROFF_TIMEOUT || ALL(HAS_WIRED_LCD, PRINTER_EVENT_LEDS) || (HAS_BACKLIGHT_TIMEOUT && defined(NEOPIXEL_BKGD_INDEX_FIRST))
#include "../feature/leds/leds.h"
#endif
@@ -319,7 +319,7 @@ void MarlinUI::init() {
#include "../feature/power_monitor.h"
#endif
- #if LED_POWEROFF_TIMEOUT > 0
+ #if HAS_LED_POWEROFF_TIMEOUT
#include "../feature/power.h"
#endif
@@ -943,9 +943,7 @@ void MarlinUI::init() {
static uint16_t max_display_update_time = 0;
const millis_t ms = millis();
- #if LED_POWEROFF_TIMEOUT > 0
- leds.update_timeout(powerManager.psu_on);
- #endif
+ TERN_(HAS_LED_POWEROFF_TIMEOUT, leds.update_timeout(powerManager.psu_on));
#if HAS_MARLINUI_MENU
@@ -1085,10 +1083,8 @@ void MarlinUI::init() {
refresh(LCDVIEW_REDRAW_NOW);
- #if LED_POWEROFF_TIMEOUT > 0
- if (!powerManager.psu_on) leds.reset_timeout(ms);
- #endif
- } // encoder activity
+ TERN_(HAS_LED_POWEROFF_TIMEOUT, if (!powerManager.psu_on) leds.reset_timeout(ms));
+ } // encoder or click
#endif // HAS_ENCODER_ACTION
@@ -1828,13 +1824,14 @@ void MarlinUI::host_notify(const char * const cstr) {
#endif
void MarlinUI::media_changed(const uint8_t old_status, const uint8_t status) {
+ TERN_(HAS_DISPLAY_SLEEP, refresh_screen_timeout());
if (old_status == status) {
TERN_(EXTENSIBLE_UI, ExtUI::onMediaError()); // Failed to mount/unmount
return;
}
- if (status) {
- if (old_status < 2) {
+ if (old_status < 2) { // Skip this section on first boot check
+ if (status) { // Media Mounted
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMediaMounted();
#elif ENABLED(BROWSE_MEDIA_ON_INSERT)
@@ -1845,16 +1842,16 @@ void MarlinUI::host_notify(const char * const cstr) {
LCD_MESSAGE(MSG_MEDIA_INSERTED);
#endif
}
- }
- else {
- if (old_status < 2) {
+ else { // Media Removed
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMediaRemoved();
- #elif HAS_SD_DETECT
+ #elif HAS_SD_DETECT // Q: Does "Media Removed" need to be shown for manual release too?
LCD_MESSAGE(MSG_MEDIA_REMOVED);
#if HAS_MARLINUI_MENU
- if (!defer_return_to_status) return_to_status();
+ if (ENABLED(HAS_WIRED_LCD) || !defer_return_to_status) return_to_status();
#endif
+ #elif HAS_WIRED_LCD
+ return_to_status();
#endif
}
}
@@ -1863,14 +1860,10 @@ void MarlinUI::host_notify(const char * const cstr) {
refresh();
- #if HAS_WIRED_LCD || LED_POWEROFF_TIMEOUT > 0
+ #if HAS_WIRED_LCD || HAS_LED_POWEROFF_TIMEOUT
const millis_t ms = millis();
- #endif
-
- TERN_(HAS_WIRED_LCD, next_lcd_update_ms = ms + LCD_UPDATE_INTERVAL); // Delay LCD update for SD activity
-
- #if LED_POWEROFF_TIMEOUT > 0
- leds.reset_timeout(ms);
+ TERN_(HAS_WIRED_LCD, next_lcd_update_ms = ms + LCD_UPDATE_INTERVAL); // Delay LCD update for SD activity
+ TERN_(HAS_LED_POWEROFF_TIMEOUT, leds.reset_timeout(ms));
#endif
}
diff --git a/Marlin/src/lcd/marlinui.h b/Marlin/src/lcd/marlinui.h
index cc130327a96b..14bf42700f2c 100644
--- a/Marlin/src/lcd/marlinui.h
+++ b/Marlin/src/lcd/marlinui.h
@@ -304,6 +304,7 @@ class MarlinUI {
static void refresh_screen_timeout();
#endif
+ // Sleep or wake the display (e.g., by turning the backlight off/on).
static void sleep_display(const bool=true) IF_DISABLED(HAS_DISPLAY_SLEEP, {});
static void wake_display() { sleep_display(false); }
@@ -743,7 +744,7 @@ class MarlinUI {
static void draw_select_screen_prompt(FSTR_P const fpre, const char * const string=nullptr, FSTR_P const fsuf=nullptr);
- #else
+ #else // !HAS_MARLINUI_MENU
static void return_to_status() {}
@@ -753,7 +754,7 @@ class MarlinUI {
FORCE_INLINE static void run_current_screen() { status_screen(); }
#endif
- #endif
+ #endif // !HAS_MARLINUI_MENU
#if ANY(HAS_MARLINUI_MENU, EXTENSIBLE_UI)
static bool lcd_clicked;
diff --git a/Marlin/src/lcd/menu/menu_advanced.cpp b/Marlin/src/lcd/menu/menu_advanced.cpp
index b7825949c07a..903c857f59f4 100644
--- a/Marlin/src/lcd/menu/menu_advanced.cpp
+++ b/Marlin/src/lcd/menu/menu_advanced.cpp
@@ -700,7 +700,7 @@ void menu_advanced_settings() {
// M593 - Acceleration items
#if ENABLED(SHAPING_MENU)
- if (!is_busy) SUBMENU(MSG_INPUT_SHAPING, menu_advanced_input_shaping);
+ SUBMENU(MSG_INPUT_SHAPING, menu_advanced_input_shaping);
#endif
#if ENABLED(CLASSIC_JERK)
diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp
index e7786749b5fc..e9aafa49c847 100644
--- a/Marlin/src/module/endstops.cpp
+++ b/Marlin/src/module/endstops.cpp
@@ -1307,7 +1307,7 @@ void Endstops::update() {
ES_REPORT_CHANGE(Z_MIN_PROBE);
#endif
#if USE_CALIBRATION
- ES_REPORT_STATE(CALIBRATION);
+ ES_REPORT_CHANGE(CALIBRATION);
#endif
#if USE_X2_MIN
ES_REPORT_CHANGE(X2_MIN);
diff --git a/Marlin/src/module/endstops.h b/Marlin/src/module/endstops.h
index 7a6da5eefa8a..1a76a26a3fe9 100644
--- a/Marlin/src/module/endstops.h
+++ b/Marlin/src/module/endstops.h
@@ -31,7 +31,7 @@
#define _ES_ENUM(A,M) A##_##M
#define ES_ENUM(A,M) _ES_ENUM(A,M)
-#define _ES_ITEM(N) N,
+#define _ES_ITEM(N) , N
#define ES_ITEM(K,N) TERN(K,_ES_ITEM,_IF_1_ELSE)(N)
#define _ESN_ITEM(K,A,M) ES_ITEM(K,ES_ENUM(A,M))
@@ -57,7 +57,9 @@
* - Z_MIN_PROBE is an alias to Z_MIN when the Z_MIN_PIN is being used as the probe pin.
* - When homing with the probe Z_ENDSTOP is a Z_MIN_PROBE alias, otherwise a Z_MIN/MAX alias.
*/
-enum EndstopEnum : char {
+enum EndstopEnum : int8_t {
+ _ES_START_ = -1
+
// Common XYZ (ABC) endstops.
ES_MINMAX(X) ES_MINMAX(Y) ES_MINMAX(Z)
ES_MINMAX(I) ES_MINMAX(J) ES_MINMAX(K)
@@ -70,12 +72,18 @@ enum EndstopEnum : char {
ES_ITEM(HAS_CALIBRATION_STATE, CALIBRATION)
// Bed Probe state is distinct or shared with Z_MIN (i.e., when the probe is the only Z endstop)
- ES_ITEM(HAS_Z_PROBE_STATE, Z_MIN_PROBE IF_DISABLED(USE_Z_MIN_PROBE, = Z_MIN))
+ #if HAS_Z_PROBE_STATE && USE_Z_MIN_PROBE
+ , Z_MIN_PROBE
+ #endif
- // The total number of states
- NUM_ENDSTOP_STATES
+ // The total number of distinct states
+ , NUM_ENDSTOP_STATES
// Endstop aliases
+ #if HAS_Z_PROBE_STATE && !USE_Z_MIN_PROBE
+ , Z_MIN_PROBE = Z_MIN
+ #endif
+
#if HAS_X_STATE
, X_ENDSTOP = TERN(X_HOME_TO_MAX, X_MAX, X_MIN)
#endif
@@ -88,7 +96,6 @@ enum EndstopEnum : char {
#if HAS_Y2_STATE
, Y2_ENDSTOP = TERN(Y_HOME_TO_MAX, Y2_MAX, Y2_MIN)
#endif
-
#if HOMING_Z_WITH_PROBE
, Z_ENDSTOP = Z_MIN_PROBE // "Z" endstop alias when homing with the probe
#elif HAS_Z_STATE
@@ -258,7 +265,7 @@ class Endstops {
#if ENABLED(CALIBRATION_GCODE)
static volatile bool calibration_probe_enabled;
static volatile bool calibration_stop_state;
- static void enable_calibration_probe(const bool onoff,const bool stop_state = true);
+ static void enable_calibration_probe(const bool onoff, const bool stop_state=true);
#endif
static void resync();
diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp
index 473d54cc382b..4bbb182bc345 100644
--- a/Marlin/src/module/motion.cpp
+++ b/Marlin/src/module/motion.cpp
@@ -257,7 +257,7 @@ void report_current_position_projected() {
AutoReporter position_auto_reporter;
#endif
-#if ANY(FULL_REPORT_TO_HOST_FEATURE, REALTIME_REPORTING_COMMANDS)
+#if ENABLED(REALTIME_REPORTING_COMMANDS)
M_StateEnum M_State_grbl = M_INIT;
@@ -299,18 +299,18 @@ void report_current_position_projected() {
*/
M_StateEnum grbl_state_for_marlin_state() {
switch (marlin_state) {
- case MF_INITIALIZING: return M_INIT;
- case MF_SD_COMPLETE: return M_ALARM;
- case MF_WAITING: return M_IDLE;
- case MF_STOPPED: return M_END;
- case MF_RUNNING: return M_RUNNING;
- case MF_PAUSED: return M_HOLD;
- case MF_KILLED: return M_ERROR;
- default: return M_IDLE;
+ case MarlinState::MF_INITIALIZING: return M_INIT;
+ case MarlinState::MF_SD_COMPLETE: return M_ALARM;
+ case MarlinState::MF_WAITING: return M_IDLE;
+ case MarlinState::MF_STOPPED: return M_END;
+ case MarlinState::MF_RUNNING: return M_RUNNING;
+ case MarlinState::MF_PAUSED: return M_HOLD;
+ case MarlinState::MF_KILLED: return M_ERROR;
+ default: return M_IDLE;
}
}
-#endif
+#endif // REALTIME_REPORTING_COMMANDS
#if IS_KINEMATIC
diff --git a/Marlin/src/module/motion.h b/Marlin/src/module/motion.h
index f9748871f5c8..bb8f36446b0c 100644
--- a/Marlin/src/module/motion.h
+++ b/Marlin/src/module/motion.h
@@ -272,7 +272,7 @@ void report_current_position_projected();
extern AutoReporter position_auto_reporter;
#endif
-#if ANY(FULL_REPORT_TO_HOST_FEATURE, REALTIME_REPORTING_COMMANDS)
+#if ENABLED(REALTIME_REPORTING_COMMANDS)
#define HAS_GRBL_STATE 1
/**
* Machine states for GRBL or TinyG
@@ -305,11 +305,9 @@ void report_current_position_projected();
}
#endif
- #if ENABLED(REALTIME_REPORTING_COMMANDS)
- void quickpause_stepper();
- void quickresume_stepper();
- #endif
-#endif
+ void quickpause_stepper();
+ void quickresume_stepper();
+#endif // REALTIME_REPORTING_COMMANDS
float get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXES, bool &is_cartesian_move));
diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp
index b4546e0235bf..b3dc856a0a58 100644
--- a/Marlin/src/module/planner.cpp
+++ b/Marlin/src/module/planner.cpp
@@ -729,8 +729,6 @@ void Planner::init() {
#endif
#endif
-#define MINIMAL_STEP_RATE 120
-
/**
* Get the current block for processing
* and mark the block as busy.
@@ -796,13 +794,9 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
uint32_t initial_rate = entry_speed ? LROUND(entry_speed * spmm) : block->initial_rate,
final_rate = LROUND(exit_speed * spmm);
- // Removing code to constrain values produces judder in direction-switching moves because the
- // current discrete stepping math diverges from physical motion under constant acceleration
- // when acceleration_steps_per_s2 is large compared to initial/final_rate.
- NOLESS(initial_rate, long(MINIMAL_STEP_RATE));
- NOLESS(final_rate, long(MINIMAL_STEP_RATE));
- NOMORE(initial_rate, block->nominal_rate); // NOTE: The nominal rate may be less than MINIMAL_STEP_RATE!
- NOMORE(final_rate, block->nominal_rate);
+ NOLESS(initial_rate, stepper.minimal_step_rate);
+ NOLESS(final_rate, stepper.minimal_step_rate);
+ NOLESS(block->nominal_rate, stepper.minimal_step_rate);
#if ANY(S_CURVE_ACCELERATION, LIN_ADVANCE)
// If we have some plateau time, the cruise rate will be the nominal rate
@@ -1201,16 +1195,20 @@ void Planner::recalculate(const_float_t safe_exit_speed_sqr) {
void Planner::kickstart_fan(uint8_t (&fan_speed)[FAN_COUNT], const millis_t &ms, const uint8_t f) {
static millis_t fan_kick_end[FAN_COUNT] = { 0 };
+ #if ENABLED(FAN_KICKSTART_LINEAR)
+ static uint8_t set_fan_speed[FAN_COUNT] = { 0 };
+ #endif
if (fan_speed[f] > FAN_OFF_PWM) {
- if (fan_kick_end[f] == 0) {
- fan_kick_end[f] = ms + FAN_KICKSTART_TIME;
+ const bool first_kick = fan_kick_end[f] == 0 && TERN1(FAN_KICKSTART_LINEAR, fan_speed[f] > set_fan_speed[f]);
+ if (first_kick)
+ fan_kick_end[f] = ms + (FAN_KICKSTART_TIME) TERN_(FAN_KICKSTART_LINEAR, * (fan_speed[f] - set_fan_speed[f]) / 255);
+ if (first_kick || PENDING(ms, fan_kick_end[f])) {
fan_speed[f] = FAN_KICKSTART_POWER;
+ return;
}
- else if (PENDING(ms, fan_kick_end[f]))
- fan_speed[f] = FAN_KICKSTART_POWER;
}
- else
- fan_kick_end[f] = 0;
+ fan_kick_end[f] = 0;
+ TERN_(FAN_KICKSTART_LINEAR, set_fan_speed[f] = fan_speed[f]);
}
#endif
diff --git a/Marlin/src/module/settings.cpp b/Marlin/src/module/settings.cpp
index d4467b76877d..a553d56745ad 100644
--- a/Marlin/src/module/settings.cpp
+++ b/Marlin/src/module/settings.cpp
@@ -1823,7 +1823,7 @@ void MarlinSettings::postprocess() {
EEPROM_Error eeprom_error = ERR_EEPROM_NOERR;
const EEPROM_Error check = check_version();
- if (check == ERR_EEPROM_VERSION) return eeprom_error;
+ if (check == ERR_EEPROM_NOPROM) return eeprom_error;
uint16_t stored_crc;
diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp
index f5a9f6d0a8ae..9da12dee1186 100644
--- a/Marlin/src/module/stepper.cpp
+++ b/Marlin/src/module/stepper.cpp
@@ -2201,12 +2201,10 @@ hal_timer_t Stepper::calc_timer_interval(uint32_t step_rate) {
#ifdef CPU_32_BIT
// A fast processor can just do integer division
- constexpr uint32_t min_step_rate = uint32_t(STEPPER_TIMER_RATE) / HAL_TIMER_TYPE_MAX;
- return step_rate > min_step_rate ? uint32_t(STEPPER_TIMER_RATE) / step_rate : HAL_TIMER_TYPE_MAX;
+ return step_rate > minimal_step_rate ? uint32_t(STEPPER_TIMER_RATE) / step_rate : HAL_TIMER_TYPE_MAX;
#else
- constexpr uint32_t min_step_rate = (F_CPU) / 500000U; // i.e., 32 or 40
if (step_rate >= 0x0800) { // higher step rate
// AVR is able to keep up at around 65kHz Stepping ISR rate at most.
// So values for step_rate > 65535 might as well be truncated.
@@ -2220,8 +2218,8 @@ hal_timer_t Stepper::calc_timer_interval(uint32_t step_rate) {
const uint8_t gain = uint8_t(pgm_read_byte(table_address + 2));
return base - MultiU8X8toH8(uint8_t(step_rate & 0x00FF), gain);
}
- else if (step_rate > min_step_rate) { // lower step rates
- step_rate -= min_step_rate; // Correct for minimal speed
+ else if (step_rate > minimal_step_rate) { // lower step rates
+ step_rate -= minimal_step_rate; // Correct for minimal speed
const uintptr_t table_address = uintptr_t(&speed_lookuptable_slow[uint8_t(step_rate >> 3)]);
return uint16_t(pgm_read_word(table_address))
- ((uint16_t(pgm_read_word(table_address + 2)) * uint8_t(step_rate & 0x0007)) >> 3);
diff --git a/Marlin/src/module/stepper.h b/Marlin/src/module/stepper.h
index 3586c23e7060..2a171bebd0be 100644
--- a/Marlin/src/module/stepper.h
+++ b/Marlin/src/module/stepper.h
@@ -295,6 +295,16 @@ class Stepper {
public:
+ // The minimal step rate ensures calculations stay within limits
+ // and avoid the most unreasonably slow step rates.
+ static constexpr uint32_t minimal_step_rate = (
+ #ifdef CPU_32_BIT
+ _MAX((STEPPER_TIMER_RATE) / HAL_TIMER_TYPE_MAX, 1U) // 32-bit shouldn't go below 1
+ #else
+ (F_CPU) / 500000U // AVR shouldn't go below 32 (16MHz) or 40 (20MHz)
+ #endif
+ );
+
#if ANY(HAS_EXTRA_ENDSTOPS, Z_STEPPER_AUTO_ALIGN)
static bool separate_multi_axis;
#endif
diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp
index 16b43f62f098..1b8ebeea6964 100644
--- a/Marlin/src/module/temperature.cpp
+++ b/Marlin/src/module/temperature.cpp
@@ -1430,7 +1430,7 @@ int16_t Temperature::getHeaterPower(const heater_id_t heater_id) {
//
inline void loud_kill(FSTR_P const lcd_msg, const heater_id_t heater_id) {
- marlin_state = MF_KILLED;
+ marlin_state = MarlinState::MF_KILLED;
thermalManager.disable_all_heaters();
#if HAS_BEEPER
for (uint8_t i = 20; i--;) {
@@ -2077,7 +2077,7 @@ void Temperature::mintemp_error(const heater_id_t heater_id OPTARG(ERR_INCLUDE_T
* - Update the heated bed PID output value
*/
void Temperature::task() {
- if (marlin_state == MF_INITIALIZING) return hal.watchdog_refresh(); // If Marlin isn't started, at least reset the watchdog!
+ if (marlin_state == MarlinState::MF_INITIALIZING) return hal.watchdog_refresh(); // If Marlin isn't started, at least reset the watchdog!
static bool no_reentry = false; // Prevent recursion
if (no_reentry) return;
diff --git a/Marlin/src/pins/pins.h b/Marlin/src/pins/pins.h
index a9349910d79c..1a356b17d785 100644
--- a/Marlin/src/pins/pins.h
+++ b/Marlin/src/pins/pins.h
@@ -564,15 +564,15 @@
#elif MB(BTT_SKR_MINI_E3_V3_0)
#include "stm32g0/pins_BTT_SKR_MINI_E3_V3_0.h" // STM32G0 env:STM32G0B1RE_btt env:STM32G0B1RE_btt_xfer
#elif MB(BTT_MANTA_M4P_V2_1)
- #include "stm32g0/pins_BTT_MANTA_M4P_V2_1.h" // STM32G0 env:STM32G0B1RE_manta_btt env:STM32G0B1RE_manta_btt_xfer
+ #include "stm32g0/pins_BTT_MANTA_M4P_V2_1.h" // STM32G0 env:STM32G0B1RE_manta_btt
#elif MB(BTT_MANTA_M5P_V1_0)
- #include "stm32g0/pins_BTT_MANTA_M5P_V1_0.h" // STM32G0 env:STM32G0B1RE_manta_btt env:STM32G0B1RE_manta_btt_xfer
+ #include "stm32g0/pins_BTT_MANTA_M5P_V1_0.h" // STM32G0 env:STM32G0B1RE_manta_btt
#elif MB(BTT_MANTA_E3_EZ_V1_0)
- #include "stm32g0/pins_BTT_MANTA_E3_EZ_V1_0.h" // STM32G0 env:STM32G0B1RE_manta_btt env:STM32G0B1RE_manta_btt_xfer
+ #include "stm32g0/pins_BTT_MANTA_E3_EZ_V1_0.h" // STM32G0 env:STM32G0B1RE_manta_btt
#elif MB(BTT_MANTA_M8P_V1_0)
- #include "stm32g0/pins_BTT_MANTA_M8P_V1_0.h" // STM32G0 env:STM32G0B1VE_btt env:STM32G0B1VE_btt_xfer
+ #include "stm32g0/pins_BTT_MANTA_M8P_V1_0.h" // STM32G0 env:STM32G0B1VE_btt
#elif MB(BTT_MANTA_M8P_V1_1)
- #include "stm32g0/pins_BTT_MANTA_M8P_V1_1.h" // STM32G0 env:STM32G0B1VE_btt env:STM32G0B1VE_btt_xfer
+ #include "stm32g0/pins_BTT_MANTA_M8P_V1_1.h" // STM32G0 env:STM32G0B1VE_btt
//
// STM32 ARM Cortex-M3
@@ -980,8 +980,6 @@
#error "BOARD_BIQU_SKR_V1_1 is now BOARD_BTT_SKR_V1_1. Please update your configuration."
#elif MB(BIGTREE_SKR_V1_1)
#error "BOARD_BIGTREE_SKR_V1_1 is now BOARD_BTT_SKR_V1_1. Please update your configuration."
- #elif MB(BIGTREE_SKR_V1_2)
- #error "BOARD_BIGTREE_SKR_V1_2 is now BOARD_BTT_SKR_V1_2. Please update your configuration."
#elif MB(BIGTREE_SKR_V1_3)
#error "BOARD_BIGTREE_SKR_V1_3 is now BOARD_BTT_SKR_V1_3. Please update your configuration."
#elif MB(BIGTREE_SKR_V1_4)
diff --git a/Marlin/src/pins/ramps/pins_MKS_BASE_14.h b/Marlin/src/pins/ramps/pins_MKS_BASE_14.h
index 9877877ffdfa..43644a4506fe 100644
--- a/Marlin/src/pins/ramps/pins_MKS_BASE_14.h
+++ b/Marlin/src/pins/ramps/pins_MKS_BASE_14.h
@@ -43,10 +43,10 @@
#ifndef RGB_LED_R_PIN
#define RGB_LED_R_PIN 50
#endif
-#ifndef RGB_LED_R_PIN
+#ifndef RGB_LED_G_PIN
#define RGB_LED_G_PIN 51
#endif
-#ifndef RGB_LED_R_PIN
+#ifndef RGB_LED_B_PIN
#define RGB_LED_B_PIN 52
#endif
diff --git a/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV3.h b/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV3.h
index a256d2e7cb26..cf400335f531 100644
--- a/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV3.h
+++ b/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV3.h
@@ -206,3 +206,6 @@
#define INDEX_AUX3_PWM2 PB9
#define INDEX_AUX3_A1 PA0
#define INDEX_AUX3_A2 PA1
+
+#define RS485_TX_ENABLE_PIN PD11
+#define RS485_RX_ENABLE_PIN PD12
diff --git a/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV4.h b/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV4.h
index f7daa4c3ec41..9c374eae4444 100644
--- a/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV4.h
+++ b/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV4.h
@@ -206,3 +206,6 @@
#define LUMEN_AUX3_PWM2 PB9
#define LUMEN_AUX3_A1 PA0
#define LUMEN_AUX3_A2 PA1
+
+#define RS485_TX_ENABLE_PIN PD11
+#define RS485_RX_ENABLE_PIN PD12
diff --git a/Marlin/src/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp
index 14319940931b..c13ba37a96ef 100644
--- a/Marlin/src/sd/cardreader.cpp
+++ b/Marlin/src/sd/cardreader.cpp
@@ -497,7 +497,7 @@ void CardReader::mount() {
cdroot();
else {
#if ANY(HAS_SD_DETECT, USB_FLASH_DRIVE_SUPPORT)
- if (marlin_state != MF_INITIALIZING) LCD_ALERTMESSAGE(MSG_MEDIA_INIT_FAIL);
+ if (marlin_state != MarlinState::MF_INITIALIZING) LCD_ALERTMESSAGE(MSG_MEDIA_INIT_FAIL);
#endif
}
@@ -1412,8 +1412,8 @@ void CardReader::fileHasFinished() {
endFilePrintNow(TERN_(SD_RESORT, true));
- flag.sdprintdone = true; // Stop getting bytes from the SD card
- marlin_state = MF_SD_COMPLETE; // Tell Marlin to enqueue M1001 soon
+ flag.sdprintdone = true; // Stop getting bytes from the SD card
+ marlin_state = MarlinState::MF_SD_COMPLETE; // Tell Marlin to enqueue M1001 soon
}
#if ENABLED(AUTO_REPORT_SD_STATUS)
diff --git a/README.md b/README.md
index b11a1193b976..77a23eaf2f70 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,7 @@ Every new HAL opens up a world of hardware. At this time we need HALs for RP2040
[Teensy 4.0](https://www.pjrc.com/store/teensy40.html)|ARM® Cortex-M7|
[Teensy 4.1](https://www.pjrc.com/store/teensy41.html)|ARM® Cortex-M7|
Linux Native|x86/ARM/etc.|Raspberry Pi
+ [All supported boards](https://marlinfw.org/docs/hardware/boards.html#boards-list)|All platforms|All boards
## Marlin Support
diff --git a/buildroot/share/PlatformIO/scripts/preflight-checks.py b/buildroot/share/PlatformIO/scripts/preflight-checks.py
index 731537f3ed85..307c2fb33f33 100644
--- a/buildroot/share/PlatformIO/scripts/preflight-checks.py
+++ b/buildroot/share/PlatformIO/scripts/preflight-checks.py
@@ -78,12 +78,15 @@ def sanity_check_target():
( build_env, motherboard, ", ".join([ e[4:] for e in board_envs if e.startswith("env:") ]) )
raise SystemExit(err)
+ # Useful values
+ project_dir = Path(env['PROJECT_DIR'])
+ config_files = ("Configuration.h", "Configuration_adv.h")
+
#
# Check for Config files in two common incorrect places
#
- epath = Path(env['PROJECT_DIR'])
- for p in [ epath, epath / "config" ]:
- for f in ("Configuration.h", "Configuration_adv.h"):
+ for p in (project_dir, project_dir / "config"):
+ for f in config_files:
if (p / f).is_file():
err = "ERROR: Config files found in directory %s. Please move them into the Marlin subfolder." % p
raise SystemExit(err)
@@ -114,11 +117,11 @@ def rm_ofile(subdir, name):
# Check for old files indicating an entangled Marlin (mixing old and new code)
#
mixedin = []
- p = Path(env['PROJECT_DIR'], "Marlin/src/lcd/dogm")
+ p = project_dir / "Marlin/src/lcd/dogm"
for f in [ "ultralcd_DOGM.cpp", "ultralcd_DOGM.h" ]:
if (p / f).is_file():
mixedin += [ f ]
- p = Path(env['PROJECT_DIR'], "Marlin/src/feature/bedlevel/abl")
+ p = project_dir / "Marlin/src/feature/bedlevel/abl"
for f in [ "abl.cpp", "abl.h" ]:
if (p / f).is_file():
mixedin += [ f ]
@@ -137,4 +140,22 @@ def rm_ofile(subdir, name):
err = "ERROR: FILAMENT_RUNOUT_SCRIPT needs a %c parameter (e.g., \"M600 T%c\") when NUM_RUNOUT_SENSORS is > 1"
raise SystemExit(err)
+ #
+ # Update old macros BOTH and EITHER in configuration files
+ #
+ conf_modified = False
+ for f in config_files:
+ conf_path = project_dir / "Marlin" / f
+ if conf_path.is_file():
+ with open(conf_path, 'r', encoding="utf8") as file:
+ text = file.read()
+ modified_text = text.replace("BOTH(", "ALL(").replace("EITHER(", "ANY(")
+ if text != modified_text:
+ conf_modified = True
+ with open(conf_path, 'w') as file:
+ file.write(modified_text)
+
+ if conf_modified:
+ raise SystemExit('WARNING: Configuration files needed an update to remove incompatible items. Try the build again to use the updated files.')
+
sanity_check_target()
diff --git a/buildroot/tests/Opulo_Lumen_REV3 b/buildroot/tests/Opulo_Lumen_REV3
index f12f69011e78..436a71e64418 100755
--- a/buildroot/tests/Opulo_Lumen_REV3
+++ b/buildroot/tests/Opulo_Lumen_REV3
@@ -8,6 +8,7 @@ set -e
use_example_configs Opulo/Lumen_REV3
opt_disable TMC_DEBUG
+opt_set RS485_SERIAL_PORT 2 RS485_BUS_BUFFER_SIZE 128
exec_test $1 $2 "Opulo Lumen REV3 Pick-and-Place" "$3"
# cleanup
diff --git a/ini/features.ini b/ini/features.ini
index b81c5285bf0b..375c26ffb782 100644
--- a/ini/features.ini
+++ b/ini/features.ini
@@ -12,7 +12,7 @@
# The order of the features matters for source-filter resolution inside of common-dependencies.py.
[features]
-YHCB2004 = red-scorp/LiquidCrystal_AIP31068@^1.0.4, red-scorp/SoftSPIB@^1.1.1
+YHCB2004 = LiquidCrystal_AIP31068=https://github.com/ellensp/LiquidCrystal_AIP31068/archive/3fc43b7.zip, red-scorp/SoftSPIB@^1.1.1
HAS_TFT_LVGL_UI = lvgl=https://github.com/makerbase-mks/LVGL-6.1.1-MKS/archive/a3ebe98bc6.zip
build_src_filter=+
extra_scripts=download_mks_assets.py
@@ -23,7 +23,6 @@ MKS_WIFI_MODULE = QRCode=https://github.com/makerbase-mks
HAS_TRINAMIC_CONFIG = TMCStepper@~0.7.3
build_src_filter=+ + + + +
HAS_T(RINAMIC_CONFIG|MC_SPI) = build_src_filter=+
-HAS_STEALTHCHOP = build_src_filter=+
SR_LCD_3W_NL = SailfishLCD=https://github.com/mikeshub/SailfishLCD/archive/6f53c19a8a.zip
HAS_MOTOR_CURRENT_(I2C|DAC|SPI|PWM) = build_src_filter=+
HAS_MOTOR_CURRENT_I2C = SlowSoftI2CMaster
@@ -317,6 +316,7 @@ NONLINEAR_EXTRUSION = build_src_filter=+ +
PARK_HEAD_ON_PAUSE = build_src_filter=+
FILAMENT_LOAD_UNLOAD_GCODES = build_src_filter=+
+HAS_STEALTHCHOP = build_src_filter=+
CNC_WORKSPACE_PLANES = build_src_filter=+
CNC_COORDINATE_SYSTEMS = build_src_filter=+
HAS_HOME_OFFSET = build_src_filter=+
@@ -334,6 +334,8 @@ HAS_LCD_CONTRAST = build_src_filter=+
HAS_LCD_BRIGHTNESS = build_src_filter=+
HAS_SOUND = build_src_filter=+
+HAS_RS485_SERIAL = jnesselr/rs485@^0.0.9
+ build_src_filter=+ +
HAS_MULTI_LANGUAGE = build_src_filter=+
TOUCH_SCREEN_CALIBRATION = build_src_filter=+
ARC_SUPPORT = build_src_filter=+
diff --git a/ini/renamed.ini b/ini/renamed.ini
index 85f2df286273..5540374c685e 100644
--- a/ini/renamed.ini
+++ b/ini/renamed.ini
@@ -98,3 +98,9 @@ extends = renamed
[env:STM32F446_tronxy] ;=> TRONXY_CXY_446_V10
extends = renamed
+
+[env:STM32G0B1RE_manta_btt_xfer] ;=> STM32G0B1RE_manta_btt
+extends = renamed
+
+[env:STM32G0B1VE_btt_xfer] ;=> STM32G0B1VE_btt
+extends = renamed
diff --git a/ini/stm32g0.ini b/ini/stm32g0.ini
index 77bdde7438dd..40493b3ff1e5 100644
--- a/ini/stm32g0.ini
+++ b/ini/stm32g0.ini
@@ -88,21 +88,6 @@ extends = env:STM32G0B1RE_btt
build_flags = ${env:STM32G0B1RE_btt.build_flags}
-DPIN_SERIAL3_RX=PD_9 -DPIN_SERIAL3_TX=PD_8 -DENABLE_HWSERIAL3
-#
-# BigTreeTech Manta M4P V2.1 (STM32G0B0RET6 ARM Cortex-M0+)
-# BigTreeTech Manta E3 EZ V1.0 / Manta M5P V1.0 (STM32G0B1RET6 ARM Cortex-M0+)
-# Custom upload to SD via Marlin with Binary Protocol
-# Requires Marlin with BINARY_FILE_TRANSFER already installed on the target board.
-# If CUSTOM_FIRMWARE_UPLOAD is also installed, Marlin will reboot the board to install the firmware.
-# Currently CUSTOM_FIRMWARE_UPLOAD must also be enabled to use 'xfer' build envs.
-#
-[env:STM32G0B1RE_manta_btt_xfer]
-extends = env:STM32G0B1RE_manta_btt
-build_flags = ${env:STM32G0B1RE_manta_btt.build_flags} -DXFER_BUILD
-extra_scripts = ${env:STM32G0B1RE_manta_btt.extra_scripts}
- pre:buildroot/share/scripts/upload.py
-upload_protocol = custom
-
#
# BigTreeTech Manta M8P V1.x (STM32G0B1VET6 ARM Cortex-M0+)
#
@@ -123,14 +108,3 @@ build_flags = ${stm32_variant.build_flags}
-Wl,--no-warn-rwx-segment
upload_protocol = stlink
debug_tool = stlink
-
-#
-# BigTreeTech Manta M8P V1.x (STM32G0B1VET6 ARM Cortex-M0+)
-# Custom upload to SD via Marlin with Binary Protocol
-#
-[env:STM32G0B1VE_btt_xfer]
-extends = env:STM32G0B1VE_btt
-build_flags = ${env:STM32G0B1VE_btt.build_flags} -DXFER_BUILD
-extra_scripts = ${env:STM32G0B1VE_btt.extra_scripts}
- pre:buildroot/share/scripts/upload.py
-upload_protocol = custom