diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..57a5b2fb5e
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,23 @@
+# editorconfig.org
+root = true
+
+[{*.patch,syntax_test_*}]
+trim_trailing_whitespace = false
+
+[{*.c,*.cpp,*.h,*.ino}]
+charset = utf-8
+
+[{*.c,*.cpp,*.h,*.ino,Makefile}]
+trim_trailing_whitespace = true
+insert_final_newline = true
+end_of_line = lf
+indent_style = space
+indent_size = 2
+
+[{*.py}]
+indent_style = space
+indent_size = 4
+
+[{*.conf,*.sublime-project}]
+indent_style = tab
+indent_size = 4
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000..83897cba6e
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,21 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto
+
+# Files with Unix line endings
+*.c text eol=lf
+*.cpp text eol=lf
+*.h text eol=lf
+*.ino text eol=lf
+*.py text eol=lf
+*.sh text eol=lf
+*.scad text eol=lf
+
+# Files with native line endings
+# *.sln text
+
+# Binary files
+*.png binary
+*.jpg binary
+*.fon binary
+*.bin binary
+*.woff binary
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000..555bde8622
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,173 @@
+#
+# 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 .
+#
+
+# Generated files
+_Version.h
+bdf2u8g.exe
+genpages.exe
+marlin_config.json
+mczip.h
+*.gen
+*.sublime-workspace
+
+#
+# OS
+#
+applet/
+.DS_Store
+
+#
+# Misc
+#
+*~
+*.orig
+*.rej
+*.bak
+*.idea
+*.i
+*.ii
+*.swp
+tags
+
+#
+# C++
+#
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+*.ino.cpp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+#
+# C
+#
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+
+# PlatformIO files/dirs
+.pio*
+.pioenvs
+.piolibdeps
+.clang_complete
+.gcc-flags.json
+/lib/
+
+# Secure Credentials
+Configuration_Secure.h
+
+# Visual Studio
+*.sln
+*.vcxproj
+*.vcxproj.user
+*.vcxproj.filters
+Release/
+Debug/
+__vm/
+.vs/
+vc-fileutils.settings
+
+# Visual Studio Code
+.vscode/*
+!.vscode/extensions.json
+
+#Simulation
+imgui.ini
+eeprom.dat
+spi_flash.bin
+fs.img
+
+#cmake
+CMakeLists.txt
+src/CMakeLists.txt
+CMakeListsPrivate.txt
+build/
+
+# CLion
+cmake-build-*
+
+# Eclipse
+.project
+.cproject
+.pydevproject
+.settings
+.classpath
+
+# Python
+__pycache__
+
+# IOLogger logs
+*_log.csv
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000000..f495d14f53
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,11 @@
+{
+ // See http://go.microsoft.com/fwlink/?LinkId=827846
+ // for the documentation about the extensions.json format
+ "recommendations": [
+ "marlinfirmware.auto-build",
+ "platformio.platformio-ide"
+ ],
+ "unwantedRecommendations": [
+ "ms-vscode.cpptools-extension-pack"
+ ]
+}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000..d0495dc7d8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,52 @@
+help:
+ @echo "Tasks for local development:"
+ @echo "* tests-single-ci: Run a single test from inside the CI"
+ @echo "* tests-single-local: Run a single test locally"
+ @echo "* tests-single-local-docker: Run a single test locally, using docker-compose"
+ @echo "* tests-all-local: Run all tests locally"
+ @echo "* tests-all-local-docker: Run all tests locally, using docker-compose"
+ @echo "* setup-local-docker: Setup local docker-compose"
+ @echo ""
+ @echo "Options for testing:"
+ @echo " TEST_TARGET Set when running tests-single-*, to select the"
+ @echo " test. If you set it to ALL it will run all "
+ @echo " tests, but some of them are broken: use "
+ @echo " tests-all-* instead to run only the ones that "
+ @echo " run on GitHub CI"
+ @echo " ONLY_TEST Limit tests to only those that contain this, or"
+ @echo " the index of the test (1-based)"
+ @echo " VERBOSE_PLATFORMIO If you want the full PIO output, set any value"
+ @echo " GIT_RESET_HARD Used by CI: reset all local changes. WARNING:"
+ @echo " THIS WILL UNDO ANY CHANGES YOU'VE MADE!"
+.PHONY: help
+
+tests-single-ci:
+ export GIT_RESET_HARD=true
+ $(MAKE) tests-single-local TEST_TARGET=$(TEST_TARGET)
+.PHONY: tests-single-ci
+
+tests-single-local:
+ @if ! test -n "$(TEST_TARGET)" ; then echo "***ERROR*** Set TEST_TARGET= or use make tests-all-local" ; return 1; fi
+ export PATH="./buildroot/bin/:./buildroot/tests/:${PATH}" \
+ && export VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) \
+ && run_tests . $(TEST_TARGET) "$(ONLY_TEST)"
+.PHONY: tests-single-local
+
+tests-single-local-docker:
+ @if ! test -n "$(TEST_TARGET)" ; then echo "***ERROR*** Set TEST_TARGET= or use make tests-all-local-docker" ; return 1; fi
+ docker-compose run --rm marlin $(MAKE) tests-single-local TEST_TARGET=$(TEST_TARGET) VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) GIT_RESET_HARD=$(GIT_RESET_HARD) ONLY_TEST="$(ONLY_TEST)"
+.PHONY: tests-single-local-docker
+
+tests-all-local:
+ export PATH="./buildroot/bin/:./buildroot/tests/:${PATH}" \
+ && export VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) \
+ && for TEST_TARGET in $$(./get_test_targets.py) ; do echo "Running tests for $$TEST_TARGET" ; run_tests . $$TEST_TARGET ; done
+.PHONY: tests-all-local
+
+tests-all-local-docker:
+ docker-compose run --rm marlin $(MAKE) tests-all-local VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) GIT_RESET_HARD=$(GIT_RESET_HARD)
+.PHONY: tests-all-local-docker
+
+setup-local-docker:
+ docker-compose build
+.PHONY: setup-local-docker
diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h
new file mode 100644
index 0000000000..7f503a79a8
--- /dev/null
+++ b/Marlin/Configuration.h
@@ -0,0 +1,3494 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+// Created by configs generator for Professional firmware
+// https://github.com/mriscoc/Ender3V2S1
+
+/**
+ * Configuration.h
+ *
+ * Basic settings such as:
+ *
+ * - Type of electronics
+ * - Type of temperature sensor
+ * - Printer geometry
+ * - Endstop configuration
+ * - LCD controller
+ * - Extra features
+ *
+ * Advanced settings can be found in Configuration_adv.h
+ */
+#define CONFIGURATION_H_VERSION 02010300
+
+//===========================================================================
+//============================= Getting Started =============================
+//===========================================================================
+
+/**
+ * Here are some useful links to help get your machine configured and calibrated:
+ *
+ * Example Configs: https://github.com/MarlinFirmware/Configurations/branches/all
+ *
+ * Průša Calculator: https://blog.prusaprinters.org/calculator_3416/
+ *
+ * Calibration Guides: https://reprap.org/wiki/Calibration
+ * https://reprap.org/wiki/Triffid_Hunter%27s_Calibration_Guide
+ * https://sites.google.com/site/repraplogphase/calibration-of-your-reprap
+ * https://youtu.be/wAL9d7FgInk
+ *
+ * Calibration Objects: https://www.thingiverse.com/thing:5573
+ * https://www.thingiverse.com/thing:1278865
+ */
+
+// @section info
+
+// Author info of this build printed to the host during boot and M115
+#define STRING_CONFIG_H_AUTHOR "Miguel A. Risco-Castillo (MRiscoC)" // Who made the changes.
+#define CUSTOM_VERSION_FILE Version.h // Path from the root directory (no quotes)
+
+/**
+ * *** VENDORS PLEASE READ ***
+ *
+ * Marlin allows you to add a custom boot image for Graphical LCDs.
+ * With this option Marlin will first show your custom screen followed
+ * by the standard Marlin logo with version number and web URL.
+ *
+ * We encourage you to take advantage of this new feature and we also
+ * respectfully request that you retain the unmodified Marlin boot screen.
+ */
+
+// Show the Marlin bootscreen on startup. ** ENABLE FOR PRODUCTION **
+#define SHOW_BOOTSCREEN
+
+// Show the bitmap in Marlin/_Bootscreen.h on startup.
+//#define SHOW_CUSTOM_BOOTSCREEN
+
+// Show the bitmap in Marlin/_Statusscreen.h on the status screen.
+//#define CUSTOM_STATUS_SCREEN_IMAGE
+
+// @section machine
+
+// Choose the name from boards.h that matches your setup
+#ifndef MOTHERBOARD
+ #define MOTHERBOARD BOARD_CREALITY_V4 // Creality Board v4.2.2
+#endif
+
+/**
+ * Select the serial port on the board to use for communication with the host.
+ * This allows the connection of wireless adapters (for instance) to non-default port pins.
+ * 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]
+ */
+#define SERIAL_PORT 1 // Ender Configs
+#define NO_AUTO_ASSIGN_WARNING // Disable serial warnings
+
+/**
+ * Serial Port Baud Rate
+ * This is the default communication speed for all serial ports.
+ * Set the baud rate defaults for additional serial ports below.
+ *
+ * 250000 works in most cases, but you might try a lower speed if
+ * you commonly experience drop-outs during host printing.
+ * You may try up to 1000000 to speed up SD file transfer.
+ *
+ * :[2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000]
+ */
+#define BAUDRATE 250000 // MRiscoC increase serial performace
+
+#define BAUD_RATE_GCODE // Enable G-code M575 to set the baud rate // MRiscoC Enables change the baudrate
+
+/**
+ * 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]
+ */
+//#define SERIAL_PORT_2 -1
+//#define BAUDRATE_2 250000 // :[2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000] Enable to override BAUDRATE
+
+/**
+ * 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]
+ */
+//#define SERIAL_PORT_3 1
+//#define BAUDRATE_3 250000 // :[2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000] Enable to override BAUDRATE
+
+// Enable the Bluetooth serial interface on AT90USB devices
+//#define BLUETOOTH
+
+// Name displayed in the LCD "Ready" message and Info menu
+#define CUSTOM_MACHINE_NAME "Ender3V2-422-MM"
+
+// Printer's unique ID, used by some programs to differentiate between machines.
+// Choose your own or use a service like https://www.uuidgenerator.net/version4
+//#define MACHINE_UUID "00000000-0000-0000-0000-000000000000"
+
+// @section stepper drivers
+
+/**
+ * Stepper Drivers
+ *
+ * These settings allow Marlin to tune stepper driver timing and enable advanced options for
+ * stepper drivers that support them. You may also override timing options in Configuration_adv.h.
+ *
+ * Use TMC2208/TMC2208_STANDALONE for TMC2225 drivers and TMC2209/TMC2209_STANDALONE for TMC2226 drivers.
+ *
+ * Options: A4988, A5984, DRV8825, LV8729, TB6560, TB6600, TMC2100,
+ * TMC2130, TMC2130_STANDALONE, TMC2160, TMC2160_STANDALONE,
+ * TMC2208, TMC2208_STANDALONE, TMC2209, TMC2209_STANDALONE,
+ * TMC26X, TMC26X_STANDALONE, TMC2660, TMC2660_STANDALONE,
+ * TMC5130, TMC5130_STANDALONE, TMC5160, TMC5160_STANDALONE
+ * :['A4988', 'A5984', 'DRV8825', 'LV8729', 'TB6560', 'TB6600', 'TMC2100', 'TMC2130', 'TMC2130_STANDALONE', 'TMC2160', 'TMC2160_STANDALONE', 'TMC2208', 'TMC2208_STANDALONE', 'TMC2209', 'TMC2209_STANDALONE', 'TMC26X', 'TMC26X_STANDALONE', 'TMC2660', 'TMC2660_STANDALONE', 'TMC5130', 'TMC5130_STANDALONE', 'TMC5160', 'TMC5160_STANDALONE']
+ */
+#define X_DRIVER_TYPE TMC2208_STANDALONE // Ender Configs
+#define Y_DRIVER_TYPE TMC2208_STANDALONE // Ender Configs
+#define Z_DRIVER_TYPE TMC2208_STANDALONE // Ender Configs
+//#define X2_DRIVER_TYPE A4988
+//#define Y2_DRIVER_TYPE A4988
+//#define Z2_DRIVER_TYPE A4988
+//#define Z3_DRIVER_TYPE A4988
+//#define Z4_DRIVER_TYPE A4988
+//#define I_DRIVER_TYPE A4988
+//#define J_DRIVER_TYPE A4988
+//#define K_DRIVER_TYPE A4988
+//#define U_DRIVER_TYPE A4988
+//#define V_DRIVER_TYPE A4988
+//#define W_DRIVER_TYPE A4988
+#define E0_DRIVER_TYPE TMC2208_STANDALONE // Ender Configs
+//#define E1_DRIVER_TYPE A4988
+//#define E2_DRIVER_TYPE A4988
+//#define E3_DRIVER_TYPE A4988
+//#define E4_DRIVER_TYPE A4988
+//#define E5_DRIVER_TYPE A4988
+//#define E6_DRIVER_TYPE A4988
+//#define E7_DRIVER_TYPE A4988
+
+/**
+ * Additional Axis Settings
+ *
+ * Define AXISn_ROTATES for all axes that rotate or pivot.
+ * Rotational axis coordinates are expressed in degrees.
+ *
+ * AXISn_NAME defines the letter used to refer to the axis in (most) G-code commands.
+ * By convention the names and roles are typically:
+ * 'A' : Rotational axis parallel to X
+ * 'B' : Rotational axis parallel to Y
+ * 'C' : Rotational axis parallel to Z
+ * 'U' : Secondary linear axis parallel to X
+ * 'V' : Secondary linear axis parallel to Y
+ * 'W' : Secondary linear axis parallel to Z
+ *
+ * Regardless of these settings the axes are internally named I, J, K, U, V, W.
+ */
+#ifdef I_DRIVER_TYPE
+ #define AXIS4_NAME 'A' // :['A', 'B', 'C', 'U', 'V', 'W']
+ #define AXIS4_ROTATES
+#endif
+#ifdef J_DRIVER_TYPE
+ #define AXIS5_NAME 'B' // :['B', 'C', 'U', 'V', 'W']
+ #define AXIS5_ROTATES
+#endif
+#ifdef K_DRIVER_TYPE
+ #define AXIS6_NAME 'C' // :['C', 'U', 'V', 'W']
+ #define AXIS6_ROTATES
+#endif
+#ifdef U_DRIVER_TYPE
+ #define AXIS7_NAME 'U' // :['U', 'V', 'W']
+ //#define AXIS7_ROTATES
+#endif
+#ifdef V_DRIVER_TYPE
+ #define AXIS8_NAME 'V' // :['V', 'W']
+ //#define AXIS8_ROTATES
+#endif
+#ifdef W_DRIVER_TYPE
+ #define AXIS9_NAME 'W' // :['W']
+ //#define AXIS9_ROTATES
+#endif
+
+// @section extruder
+
+// This defines the number of extruders
+// :[0, 1, 2, 3, 4, 5, 6, 7, 8]
+#define EXTRUDERS 1
+
+// Generally expected filament diameter (1.75, 2.85, 3.0, ...). Used for Volumetric, Filament Width Sensor, etc.
+#define DEFAULT_NOMINAL_FILAMENT_DIA 1.75
+
+// For Cyclops or any "multi-extruder" that shares a single nozzle.
+//#define SINGLENOZZLE
+
+// Save and restore temperature and fan speed on tool-change.
+// Set standby for the unselected tool with M104/106/109 T...
+#if ENABLED(SINGLENOZZLE)
+ //#define SINGLENOZZLE_STANDBY_TEMP
+ //#define SINGLENOZZLE_STANDBY_FAN
+#endif
+
+// @section multi-material
+
+/**
+ * Multi-Material Unit
+ * Set to one of these predefined models:
+ *
+ * PRUSA_MMU1 : Průša MMU1 (The "multiplexer" version)
+ * PRUSA_MMU2 : Průša MMU2
+ * PRUSA_MMU2S : Průša MMU2S (Requires MK3S extruder with motion sensor, EXTRUDERS = 5)
+ * EXTENDABLE_EMU_MMU2 : MMU with configurable number of filaments (ERCF, SMuFF or similar with Průša MMU2 compatible firmware)
+ * EXTENDABLE_EMU_MMU2S : MMUS with configurable number of filaments (ERCF, SMuFF or similar with Průša MMU2 compatible firmware)
+ *
+ * Requires NOZZLE_PARK_FEATURE to park print head in case MMU unit fails.
+ * See additional options in Configuration_adv.h.
+ * :["PRUSA_MMU1", "PRUSA_MMU2", "PRUSA_MMU2S", "EXTENDABLE_EMU_MMU2", "EXTENDABLE_EMU_MMU2S"]
+ */
+//#define MMU_MODEL PRUSA_MMU2
+
+// A dual extruder that uses a single stepper motor
+//#define SWITCHING_EXTRUDER
+#if ENABLED(SWITCHING_EXTRUDER)
+ #define SWITCHING_EXTRUDER_SERVO_NR 0
+ #define SWITCHING_EXTRUDER_SERVO_ANGLES { 0, 90 } // Angles for E0, E1[, E2, E3]
+ #if EXTRUDERS > 3
+ #define SWITCHING_EXTRUDER_E23_SERVO_NR 1
+ #endif
+#endif
+
+// Switch extruders by bumping the toolhead. Requires EVENT_GCODE_TOOLCHANGE_#.
+//#define MECHANICAL_SWITCHING_EXTRUDER
+
+/**
+ * A dual-nozzle that uses a servomotor to raise/lower one (or both) of the nozzles.
+ * Can be combined with SWITCHING_EXTRUDER.
+ */
+//#define SWITCHING_NOZZLE
+#if ENABLED(SWITCHING_NOZZLE)
+ #define SWITCHING_NOZZLE_SERVO_NR 0
+ //#define SWITCHING_NOZZLE_E1_SERVO_NR 1 // If two servos are used, the index of the second
+ #define SWITCHING_NOZZLE_SERVO_ANGLES { 0, 90 } // Angles for E0, E1 (single servo) or lowered/raised (dual servo)
+ #define SWITCHING_NOZZLE_SERVO_DWELL 2500 // Dwell time to wait for servo to make physical move
+#endif
+
+// Switch nozzles by bumping the toolhead. Requires EVENT_GCODE_TOOLCHANGE_#.
+//#define MECHANICAL_SWITCHING_NOZZLE
+
+/**
+ * Two separate X-carriages with extruders that connect to a moving part
+ * via a solenoid docking mechanism. Requires SOL1_PIN and SOL2_PIN.
+ */
+//#define PARKING_EXTRUDER
+
+/**
+ * Two separate X-carriages with extruders that connect to a moving part
+ * via a magnetic docking mechanism using movements and no solenoid
+ *
+ * project : https://www.thingiverse.com/thing:3080893
+ * movements : https://youtu.be/0xCEiG9VS3k
+ * https://youtu.be/Bqbcs0CU2FE
+ */
+//#define MAGNETIC_PARKING_EXTRUDER
+
+#if EITHER(PARKING_EXTRUDER, MAGNETIC_PARKING_EXTRUDER)
+
+ #define PARKING_EXTRUDER_PARKING_X { -78, 184 } // X positions for parking the extruders
+ #define PARKING_EXTRUDER_GRAB_DISTANCE 1 // (mm) Distance to move beyond the parking point to grab the extruder
+
+ #if ENABLED(PARKING_EXTRUDER)
+
+ #define PARKING_EXTRUDER_SOLENOIDS_INVERT // If enabled, the solenoid is NOT magnetized with applied voltage
+ #define PARKING_EXTRUDER_SOLENOIDS_PINS_ACTIVE LOW // LOW or HIGH pin signal energizes the coil
+ #define PARKING_EXTRUDER_SOLENOIDS_DELAY 250 // (ms) Delay for magnetic field. No delay if 0 or not defined.
+ //#define MANUAL_SOLENOID_CONTROL // Manual control of docking solenoids with M380 S / M381
+
+ #elif ENABLED(MAGNETIC_PARKING_EXTRUDER)
+
+ #define MPE_FAST_SPEED 9000 // (mm/min) Speed for travel before last distance point
+ #define MPE_SLOW_SPEED 4500 // (mm/min) Speed for last distance travel to park and couple
+ #define MPE_TRAVEL_DISTANCE 10 // (mm) Last distance point
+ #define MPE_COMPENSATION 0 // Offset Compensation -1 , 0 , 1 (multiplier) only for coupling
+
+ #endif
+
+#endif
+
+/**
+ * Switching Toolhead
+ *
+ * Support for swappable and dockable toolheads, such as
+ * the E3D Tool Changer. Toolheads are locked with a servo.
+ */
+//#define SWITCHING_TOOLHEAD
+
+/**
+ * Magnetic Switching Toolhead
+ *
+ * Support swappable and dockable toolheads with a magnetic
+ * docking mechanism using movement and no servo.
+ */
+//#define MAGNETIC_SWITCHING_TOOLHEAD
+
+/**
+ * Electromagnetic Switching Toolhead
+ *
+ * Parking for CoreXY / HBot kinematics.
+ * Toolheads are parked at one edge and held with an electromagnet.
+ * Supports more than 2 Toolheads. See https://youtu.be/JolbsAKTKf4
+ */
+//#define ELECTROMAGNETIC_SWITCHING_TOOLHEAD
+
+#if ANY(SWITCHING_TOOLHEAD, MAGNETIC_SWITCHING_TOOLHEAD, ELECTROMAGNETIC_SWITCHING_TOOLHEAD)
+ #define SWITCHING_TOOLHEAD_Y_POS 235 // (mm) Y position of the toolhead dock
+ #define SWITCHING_TOOLHEAD_Y_SECURITY 10 // (mm) Security distance Y axis
+ #define SWITCHING_TOOLHEAD_Y_CLEAR 60 // (mm) Minimum distance from dock for unobstructed X axis
+ #define SWITCHING_TOOLHEAD_X_POS { 215, 0 } // (mm) X positions for parking the extruders
+ #if ENABLED(SWITCHING_TOOLHEAD)
+ #define SWITCHING_TOOLHEAD_SERVO_NR 2 // Index of the servo connector
+ #define SWITCHING_TOOLHEAD_SERVO_ANGLES { 0, 180 } // (degrees) Angles for Lock, Unlock
+ #elif ENABLED(MAGNETIC_SWITCHING_TOOLHEAD)
+ #define SWITCHING_TOOLHEAD_Y_RELEASE 5 // (mm) Security distance Y axis
+ #define SWITCHING_TOOLHEAD_X_SECURITY { 90, 150 } // (mm) Security distance X axis (T0,T1)
+ //#define PRIME_BEFORE_REMOVE // Prime the nozzle before release from the dock
+ #if ENABLED(PRIME_BEFORE_REMOVE)
+ #define SWITCHING_TOOLHEAD_PRIME_MM 20 // (mm) Extruder prime length
+ #define SWITCHING_TOOLHEAD_RETRACT_MM 10 // (mm) Retract after priming length
+ #define SWITCHING_TOOLHEAD_PRIME_FEEDRATE 300 // (mm/min) Extruder prime feedrate
+ #define SWITCHING_TOOLHEAD_RETRACT_FEEDRATE 2400 // (mm/min) Extruder retract feedrate
+ #endif
+ #elif ENABLED(ELECTROMAGNETIC_SWITCHING_TOOLHEAD)
+ #define SWITCHING_TOOLHEAD_Z_HOP 2 // (mm) Z raise for switching
+ #endif
+#endif
+
+/**
+ * "Mixing Extruder"
+ * - Adds G-codes M163 and M164 to set and "commit" the current mix factors.
+ * - Extends the stepping routines to move multiple steppers in proportion to the mix.
+ * - Optional support for Repetier Firmware's 'M164 S' supporting virtual tools.
+ * - This implementation supports up to two mixing extruders.
+ * - Enable DIRECT_MIXING_IN_G1 for M165 and mixing in G1 (from Pia Taubert's reference implementation).
+ */
+//#define MIXING_EXTRUDER
+#if ENABLED(MIXING_EXTRUDER)
+ #define MIXING_STEPPERS 2 // Number of steppers in your mixing extruder
+ #define MIXING_VIRTUAL_TOOLS 16 // Use the Virtual Tool method with M163 and M164
+ //#define DIRECT_MIXING_IN_G1 // Allow ABCDHI mix factors in G1 movement commands
+ //#define GRADIENT_MIX // Support for gradient mixing with M166 and LCD
+ //#define MIXING_PRESETS // Assign 8 default V-tool presets for 2 or 3 MIXING_STEPPERS
+ #if ENABLED(GRADIENT_MIX)
+ //#define GRADIENT_VTOOL // Add M166 T to use a V-tool index as a Gradient alias
+ #endif
+#endif
+
+// Offset of the extruders (uncomment if using more than one and relying on firmware to position when changing).
+// The offset has to be X=0, Y=0 for the extruder 0 hotend (default extruder).
+// For the other hotends it is their distance from the extruder 0 hotend.
+//#define HOTEND_OFFSET_X { 0.0, 20.00 } // (mm) relative X-offset for each nozzle
+//#define HOTEND_OFFSET_Y { 0.0, 5.00 } // (mm) relative Y-offset for each nozzle
+//#define HOTEND_OFFSET_Z { 0.0, 0.00 } // (mm) relative Z-offset for each nozzle
+
+// @section psu control
+
+/**
+ * Power Supply Control
+ *
+ * Enable and connect the power supply to the PS_ON_PIN.
+ * Specify whether the power supply is active HIGH or active LOW.
+ */
+//#define PSU_CONTROL
+//#define PSU_NAME "Power Supply"
+
+#if ENABLED(PSU_CONTROL)
+ //#define MKS_PWC // Using the MKS PWC add-on
+ //#define PS_OFF_CONFIRM // Confirm dialog when power off
+ //#define PS_OFF_SOUND // Beep 1s when power off
+ #define PSU_ACTIVE_STATE LOW // Set 'LOW' for ATX, 'HIGH' for X-Box
+
+ //#define PSU_DEFAULT_OFF // Keep power off until enabled directly with M80
+ //#define PSU_POWERUP_DELAY 250 // (ms) Delay for the PSU to warm up to full power
+ //#define LED_POWEROFF_TIMEOUT 10000 // (ms) Turn off LEDs after power-off, with this amount of delay
+
+ //#define POWER_OFF_TIMER // Enable M81 D to power off after a delay
+ //#define POWER_OFF_WAIT_FOR_COOLDOWN // Enable M81 S to power off only after cooldown
+
+ //#define PSU_POWERUP_GCODE "M355 S1" // G-code to run after power-on (e.g., case light on)
+ //#define PSU_POWEROFF_GCODE "M355 S0" // G-code to run before power-off (e.g., case light off)
+
+ //#define AUTO_POWER_CONTROL // Enable automatic control of the PS_ON pin
+ #if ENABLED(AUTO_POWER_CONTROL)
+ #define AUTO_POWER_FANS // Turn on PSU if fans need power
+ #define AUTO_POWER_E_FANS
+ #define AUTO_POWER_CONTROLLERFAN
+ #define AUTO_POWER_CHAMBER_FAN
+ #define AUTO_POWER_COOLER_FAN
+ #define POWER_TIMEOUT 30 // (s) Turn off power if the machine is idle for this duration
+ //#define POWER_OFF_DELAY 60 // (s) Delay of poweroff after M81 command. Useful to let fans run for extra time.
+ #endif
+ #if EITHER(AUTO_POWER_CONTROL, POWER_OFF_WAIT_FOR_COOLDOWN)
+ //#define AUTO_POWER_E_TEMP 50 // (°C) PSU on if any extruder is over this temperature
+ //#define AUTO_POWER_CHAMBER_TEMP 30 // (°C) PSU on if the chamber is over this temperature
+ //#define AUTO_POWER_COOLER_TEMP 26 // (°C) PSU on if the cooler is over this temperature
+ #endif
+#endif
+
+//===========================================================================
+//============================= Thermal Settings ============================
+//===========================================================================
+// @section temperature
+
+/**
+ * --NORMAL IS 4.7kΩ PULLUP!-- 1kΩ pullup can be used on hotend sensor, using correct resistor and table
+ *
+ * Temperature sensors available:
+ *
+ * SPI RTD/Thermocouple Boards - IMPORTANT: Read the NOTE below!
+ * -------
+ * -5 : MAX31865 with Pt100/Pt1000, 2, 3, or 4-wire (only for sensors 0-1)
+ * NOTE: You must uncomment/set the MAX31865_*_OHMS_n defines below.
+ * -3 : MAX31855 with Thermocouple, -200°C to +700°C (only for sensors 0-1)
+ * -2 : MAX6675 with Thermocouple, 0°C to +700°C (only for sensors 0-1)
+ *
+ * NOTE: Ensure TEMP_n_CS_PIN is set in your pins file for each TEMP_SENSOR_n using an SPI Thermocouple. By default,
+ * Hardware SPI on the default serial bus is used. If you have also set TEMP_n_SCK_PIN and TEMP_n_MISO_PIN,
+ * Software SPI will be used on those ports instead. You can force Hardware SPI on the default bus in the
+ * Configuration_adv.h file. At this time, separate Hardware SPI buses for sensors are not supported.
+ *
+ * Analog Themocouple Boards
+ * -------
+ * -4 : AD8495 with Thermocouple
+ * -1 : AD595 with Thermocouple
+ *
+ * Analog Thermistors - 4.7kΩ pullup - Normal
+ * -------
+ * 1 : 100kΩ EPCOS - Best choice for EPCOS thermistors
+ * 331 : 100kΩ Same as #1, but 3.3V scaled for MEGA
+ * 332 : 100kΩ Same as #1, but 3.3V scaled for DUE
+ * 2 : 200kΩ ATC Semitec 204GT-2
+ * 202 : 200kΩ Copymaster 3D
+ * 3 : ???Ω Mendel-parts thermistor
+ * 4 : 10kΩ Generic Thermistor !! DO NOT use for a hotend - it gives bad resolution at high temp. !!
+ * 5 : 100kΩ ATC Semitec 104GT-2/104NT-4-R025H42G - Used in ParCan, J-Head, and E3D, SliceEngineering 300°C
+ * 501 : 100kΩ Zonestar - Tronxy X3A
+ * 502 : 100kΩ Zonestar - used by hot bed in Zonestar Průša P802M
+ * 503 : 100kΩ Zonestar (Z8XM2) Heated Bed thermistor
+ * 504 : 100kΩ Zonestar P802QR2 (Part# QWG-104F-B3950) Hotend Thermistor
+ * 505 : 100kΩ Zonestar P802QR2 (Part# QWG-104F-3950) Bed Thermistor
+ * 512 : 100kΩ RPW-Ultra hotend
+ * 6 : 100kΩ EPCOS - Not as accurate as table #1 (created using a fluke thermocouple)
+ * 7 : 100kΩ Honeywell 135-104LAG-J01
+ * 71 : 100kΩ Honeywell 135-104LAF-J01
+ * 8 : 100kΩ Vishay 0603 SMD NTCS0603E3104FXT
+ * 9 : 100kΩ GE Sensing AL03006-58.2K-97-G1
+ * 10 : 100kΩ RS PRO 198-961
+ * 11 : 100kΩ Keenovo AC silicone mats, most Wanhao i3 machines - beta 3950, 1%
+ * 12 : 100kΩ Vishay 0603 SMD NTCS0603E3104FXT (#8) - calibrated for Makibox hot bed
+ * 13 : 100kΩ Hisens up to 300°C - for "Simple ONE" & "All In ONE" hotend - beta 3950, 1%
+ * 15 : 100kΩ Calibrated for JGAurora A5 hotend
+ * 18 : 200kΩ ATC Semitec 204GT-2 Dagoma.Fr - MKS_Base_DKU001327
+ * 22 : 100kΩ GTM32 Pro vB - hotend - 4.7kΩ pullup to 3.3V and 220Ω to analog input
+ * 23 : 100kΩ GTM32 Pro vB - bed - 4.7kΩ pullup to 3.3v and 220Ω to analog input
+ * 30 : 100kΩ Kis3d Silicone heating mat 200W/300W with 6mm precision cast plate (EN AW 5083) NTC100K - beta 3950
+ * 60 : 100kΩ Maker's Tool Works Kapton Bed Thermistor - beta 3950
+ * 61 : 100kΩ Formbot/Vivedino 350°C Thermistor - beta 3950
+ * 66 : 4.7MΩ Dyze Design / Trianglelab T-D500 500°C High Temperature Thermistor
+ * 67 : 500kΩ SliceEngineering 450°C Thermistor
+ * 68 : PT100 amplifier board from Dyze Design
+ * 70 : 100kΩ bq Hephestos 2
+ * 75 : 100kΩ Generic Silicon Heat Pad with NTC100K MGB18-104F39050L32
+ * 2000 : 100kΩ Ultimachine Rambo TDK NTCG104LH104KT1 NTC100K motherboard Thermistor
+ *
+ * Analog Thermistors - 1kΩ pullup - Atypical, and requires changing out the 4.7kΩ pullup for 1kΩ.
+ * ------- (but gives greater accuracy and more stable PID)
+ * 51 : 100kΩ EPCOS (1kΩ pullup)
+ * 52 : 200kΩ ATC Semitec 204GT-2 (1kΩ pullup)
+ * 55 : 100kΩ ATC Semitec 104GT-2 - Used in ParCan & J-Head (1kΩ pullup)
+ *
+ * Analog Thermistors - 10kΩ pullup - Atypical
+ * -------
+ * 99 : 100kΩ Found on some Wanhao i3 machines with a 10kΩ pull-up resistor
+ *
+ * Analog RTDs (Pt100/Pt1000)
+ * -------
+ * 110 : Pt100 with 1kΩ pullup (atypical)
+ * 147 : Pt100 with 4.7kΩ pullup
+ * 1010 : Pt1000 with 1kΩ pullup (atypical)
+ * 1022 : Pt1000 with 2.2kΩ pullup
+ * 1047 : Pt1000 with 4.7kΩ pullup (E3D)
+ * 20 : Pt100 with circuit in the Ultimainboard V2.x with mainboard ADC reference voltage = INA826 amplifier-board supply voltage.
+ * NOTE: (1) Must use an ADC input with no pullup. (2) Some INA826 amplifiers are unreliable at 3.3V so consider using sensor 147, 110, or 21.
+ * 21 : Pt100 with circuit in the Ultimainboard V2.x with 3.3v ADC reference voltage (STM32, LPC176x....) and 5V INA826 amplifier board supply.
+ * NOTE: ADC pins are not 5V tolerant. Not recommended because it's possible to damage the CPU by going over 500°C.
+ * 201 : Pt100 with circuit in Overlord, similar to Ultimainboard V2.x
+ *
+ * Custom/Dummy/Other Thermal Sensors
+ * ------
+ * 0 : not used
+ * 1000 : Custom - Specify parameters in Configuration_adv.h
+ *
+ * !!! Use these for Testing or Development purposes. NEVER for production machine. !!!
+ * 998 : Dummy Table that ALWAYS reads 25°C or the temperature defined below.
+ * 999 : Dummy Table that ALWAYS reads 100°C or the temperature defined below.
+ *
+ */
+#define TEMP_SENSOR_0 1
+#define TEMP_SENSOR_1 0
+#define TEMP_SENSOR_2 0
+#define TEMP_SENSOR_3 0
+#define TEMP_SENSOR_4 0
+#define TEMP_SENSOR_5 0
+#define TEMP_SENSOR_6 0
+#define TEMP_SENSOR_7 0
+#define TEMP_SENSOR_BED 1 // Ender Configs
+#define TEMP_SENSOR_PROBE 0
+#define TEMP_SENSOR_CHAMBER 0
+#define TEMP_SENSOR_COOLER 0
+#define TEMP_SENSOR_BOARD 0
+#define TEMP_SENSOR_REDUNDANT 0
+
+// Dummy thermistor constant temperature readings, for use with 998 and 999
+#define DUMMY_THERMISTOR_998_VALUE 25
+#define DUMMY_THERMISTOR_999_VALUE 100
+
+// Resistor values when using MAX31865 sensors (-5) on TEMP_SENSOR_0 / 1
+#if TEMP_SENSOR_IS_MAX_TC(0)
+ #define MAX31865_SENSOR_OHMS_0 100 // (Ω) Typically 100 or 1000 (PT100 or PT1000)
+ #define MAX31865_CALIBRATION_OHMS_0 430 // (Ω) Typically 430 for Adafruit PT100; 4300 for Adafruit PT1000
+#endif
+#if TEMP_SENSOR_IS_MAX_TC(1)
+ #define MAX31865_SENSOR_OHMS_1 100
+ #define MAX31865_CALIBRATION_OHMS_1 430
+#endif
+#if TEMP_SENSOR_IS_MAX_TC(2)
+ #define MAX31865_SENSOR_OHMS_2 100
+ #define MAX31865_CALIBRATION_OHMS_2 430
+#endif
+
+#if HAS_E_TEMP_SENSOR
+ #define TEMP_RESIDENCY_TIME 10 // (seconds) Time to wait for hotend to "settle" in M109
+ #define TEMP_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer
+ #define TEMP_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target
+#endif
+
+#if TEMP_SENSOR_BED
+ #define TEMP_BED_RESIDENCY_TIME 10 // (seconds) Time to wait for bed to "settle" in M190
+ #define TEMP_BED_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer
+ #define TEMP_BED_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target
+#endif
+
+#if TEMP_SENSOR_CHAMBER
+ #define TEMP_CHAMBER_RESIDENCY_TIME 10 // (seconds) Time to wait for chamber to "settle" in M191
+ #define TEMP_CHAMBER_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer
+ #define TEMP_CHAMBER_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target
+#endif
+
+/**
+ * Redundant Temperature Sensor (TEMP_SENSOR_REDUNDANT)
+ *
+ * Use a temp sensor as a redundant sensor for another reading. Select an unused temperature sensor, and another
+ * sensor you'd like it to be redundant for. If the two thermistors differ by TEMP_SENSOR_REDUNDANT_MAX_DIFF (°C),
+ * the print will be aborted. Whichever sensor is selected will have its normal functions disabled; i.e. selecting
+ * the Bed sensor (-1) will disable bed heating/monitoring.
+ *
+ * For selecting source/target use: COOLER, PROBE, BOARD, CHAMBER, BED, E0, E1, E2, E3, E4, E5, E6, E7
+ */
+#if TEMP_SENSOR_REDUNDANT
+ #define TEMP_SENSOR_REDUNDANT_SOURCE E1 // The sensor that will provide the redundant reading.
+ #define TEMP_SENSOR_REDUNDANT_TARGET E0 // The sensor that we are providing a redundant reading for.
+ #define TEMP_SENSOR_REDUNDANT_MAX_DIFF 10 // (°C) Temperature difference that will trigger a print abort.
+#endif
+
+// Below this temperature the heater will be switched off
+// because it probably indicates a broken thermistor wire.
+#define HEATER_0_MINTEMP 0 // Ender Configs
+#define HEATER_1_MINTEMP 5
+#define HEATER_2_MINTEMP 5
+#define HEATER_3_MINTEMP 5
+#define HEATER_4_MINTEMP 5
+#define HEATER_5_MINTEMP 5
+#define HEATER_6_MINTEMP 5
+#define HEATER_7_MINTEMP 5
+#define BED_MINTEMP 0 // Ender Configs
+#define CHAMBER_MINTEMP 5
+
+// Above this temperature the heater will be switched off.
+// This can protect components from overheating, but NOT from shorts and failures.
+// (Use MINTEMP for thermistor short/failure protection.)
+#define HEATER_0_MAXTEMP 275
+#define HEATER_1_MAXTEMP 275
+#define HEATER_2_MAXTEMP 275
+#define HEATER_3_MAXTEMP 275
+#define HEATER_4_MAXTEMP 275
+#define HEATER_5_MAXTEMP 275
+#define HEATER_6_MAXTEMP 275
+#define HEATER_7_MAXTEMP 275
+#define BED_MAXTEMP 120 // Ender3V2 Configs
+#define CHAMBER_MAXTEMP 60
+
+/**
+ * Thermal Overshoot
+ * During heatup (and printing) the temperature can often "overshoot" the target by many degrees
+ * (especially before PID tuning). Setting the target temperature too close to MAXTEMP guarantees
+ * a MAXTEMP shutdown! Use these values to forbid temperatures being set too close to MAXTEMP.
+ */
+#define HOTEND_OVERSHOOT 15 // (°C) Forbid temperatures over MAXTEMP - OVERSHOOT
+#define BED_OVERSHOOT 10 // (°C) Forbid temperatures over MAXTEMP - OVERSHOOT
+#define COOLER_OVERSHOOT 2 // (°C) Forbid temperatures closer than OVERSHOOT
+
+//===========================================================================
+//============================= PID Settings ================================
+//===========================================================================
+
+// @section hotend temp
+
+// Enable PIDTEMP for PID control or MPCTEMP for Predictive Model.
+// temperature control. Disable both for bang-bang heating.
+#define PIDTEMP // See the PID Tuning Guide at https://reprap.org/wiki/PID_Tuning
+//#define MPCTEMP // ** EXPERIMENTAL **
+
+#define BANG_MAX 255 // Limits current to nozzle while in bang-bang mode; 255=full current
+#define PID_MAX BANG_MAX // Limits current to nozzle while PID is active (see PID_FUNCTIONAL_RANGE below); 255=full current
+#define PID_K1 0.95 // Smoothing factor within any PID loop
+
+#if ENABLED(PIDTEMP)
+ //#define PID_DEBUG // Print PID debug data to the serial port. Use 'M303 D' to toggle activation.
+ //#define PID_PARAMS_PER_HOTEND // Use separate PID parameters for each extruder (useful for mismatched extruders)
+ // Set/get with G-code: M301 E[extruder number, 0-2]
+
+ #if ENABLED(PID_PARAMS_PER_HOTEND)
+ // Specify up to one value per hotend here, according to your setup.
+ // If there are fewer values, the last one applies to the remaining hotends.
+ #define DEFAULT_Kp_LIST { 22.20, 22.20 }
+ #define DEFAULT_Ki_LIST { 1.08, 1.08 }
+ #define DEFAULT_Kd_LIST { 114.00, 114.00 }
+ #else
+ #define DEFAULT_Kp 22.89
+ #define DEFAULT_Ki 1.87
+ #define DEFAULT_Kd 70.18
+ #endif
+#endif
+
+/**
+ * Model Predictive Control for hotend
+ *
+ * Use a physical model of the hotend to control temperature. When configured correctly
+ * this gives better responsiveness and stability than PID and it also removes the need
+ * for PID_EXTRUSION_SCALING and PID_FAN_SCALING. Use M306 T to autotune the model.
+ * @section mpctemp
+ */
+#if ENABLED(MPCTEMP)
+ //#define MPC_EDIT_MENU // Add MPC editing to the "Advanced Settings" menu. (~1300 bytes of flash)
+ //#define MPC_AUTOTUNE_MENU // Add MPC auto-tuning to the "Advanced Settings" menu. (~350 bytes of flash)
+
+ #define MPC_MAX BANG_MAX // (0..255) Current to nozzle while MPC is active.
+ #define MPC_HEATER_POWER { 40.0f } // (W) Heat cartridge powers.
+
+ #define MPC_INCLUDE_FAN // Model the fan speed?
+
+ // Measured physical constants from M306
+ #define MPC_BLOCK_HEAT_CAPACITY { 16.7f } // (J/K) Heat block heat capacities.
+ #define MPC_SENSOR_RESPONSIVENESS { 0.22f } // (K/s per ∆K) Rate of change of sensor temperature from heat block.
+ #define MPC_AMBIENT_XFER_COEFF { 0.068f } // (W/K) Heat transfer coefficients from heat block to room air with fan off.
+ #if ENABLED(MPC_INCLUDE_FAN)
+ #define MPC_AMBIENT_XFER_COEFF_FAN255 { 0.097f } // (W/K) Heat transfer coefficients from heat block to room air with fan on full.
+ #endif
+
+ // For one fan and multiple hotends MPC needs to know how to apply the fan cooling effect.
+ #if ENABLED(MPC_INCLUDE_FAN)
+ //#define MPC_FAN_0_ALL_HOTENDS
+ //#define MPC_FAN_0_ACTIVE_HOTEND
+ #endif
+
+ #define FILAMENT_HEAT_CAPACITY_PERMM { 5.6e-3f } // 0.0056 J/K/mm for 1.75mm PLA (0.0149 J/K/mm for 2.85mm PLA).
+ //#define FILAMENT_HEAT_CAPACITY_PERMM { 3.6e-3f } // 0.0036 J/K/mm for 1.75mm PETG (0.0094 J/K/mm for 2.85mm PETG).
+
+ // Advanced options
+ #define MPC_SMOOTHING_FACTOR 0.5f // (0.0...1.0) Noisy temperature sensors may need a lower value for stabilization.
+ #define MPC_MIN_AMBIENT_CHANGE 1.0f // (K/s) Modeled ambient temperature rate of change, when correcting model inaccuracies.
+ #define MPC_STEADYSTATE 0.5f // (K/s) Temperature change rate for steady state logic to be enforced.
+
+ #define MPC_TUNING_POS { X_CENTER, Y_CENTER, 1.0f } // (mm) M306 Autotuning position, ideally bed center at first layer height.
+ #define MPC_TUNING_END_Z 10.0f // (mm) M306 Autotuning final Z position.
+#endif
+
+//===========================================================================
+//====================== PID > Bed Temperature Control ======================
+//===========================================================================
+
+/**
+ * PID Bed Heating
+ *
+ * If this option is enabled set PID constants below.
+ * If this option is disabled, bang-bang will be used and BED_LIMIT_SWITCHING will enable hysteresis.
+ *
+ * The PID frequency will be the same as the extruder PWM.
+ * If PID_dT is the default, and correct for the hardware/configuration, that means 7.689Hz,
+ * which is fine for driving a square wave into a resistive load and does not significantly
+ * impact FET heating. This also works fine on a Fotek SSR-10DA Solid State Relay into a 250W
+ * heater. If your configuration is significantly different than this and you don't understand
+ * the issues involved, don't use bed PID until someone else verifies that your hardware works.
+ * @section bed temp
+ */
+#define PIDTEMPBED // Ender Configs
+
+//#define BED_LIMIT_SWITCHING
+
+/**
+ * Max Bed Power
+ * Applies to all forms of bed control (PID, bang-bang, and bang-bang with hysteresis).
+ * When set to any value below 255, enables a form of PWM to the bed that acts like a divider
+ * so don't use it unless you are OK with PWM on your bed. (See the comment on enabling PIDTEMPBED)
+ */
+#define MAX_BED_POWER 255 // limits duty cycle to bed; 255=full current
+
+#if ENABLED(PIDTEMPBED)
+ //#define MIN_BED_POWER 0
+ //#define PID_BED_DEBUG // Print Bed PID debug data to the serial port.
+
+ // 120V 250W silicone heater into 4mm borosilicate (MendelMax 1.5+)
+ // from FOPDT model - kp=.39 Tp=405 Tdead=66, Tc set to 79.2, aggressive factor of .15 (vs .1, 1, 10)
+ #define DEFAULT_bedKp 462.10
+ #define DEFAULT_bedKi 85.47
+ #define DEFAULT_bedKd 624.59
+
+ // FIND YOUR OWN: "M303 E-1 C8 S90" to run autotune on the bed at 90 degreesC for 8 cycles.
+#endif // PIDTEMPBED
+
+//===========================================================================
+//==================== PID > Chamber Temperature Control ====================
+//===========================================================================
+
+/**
+ * PID Chamber Heating
+ *
+ * If this option is enabled set PID constants below.
+ * If this option is disabled, bang-bang will be used and CHAMBER_LIMIT_SWITCHING will enable
+ * hysteresis.
+ *
+ * The PID frequency will be the same as the extruder PWM.
+ * If PID_dT is the default, and correct for the hardware/configuration, that means 7.689Hz,
+ * which is fine for driving a square wave into a resistive load and does not significantly
+ * impact FET heating. This also works fine on a Fotek SSR-10DA Solid State Relay into a 200W
+ * heater. If your configuration is significantly different than this and you don't understand
+ * the issues involved, don't use chamber PID until someone else verifies that your hardware works.
+ * @section chamber temp
+ */
+//#define PIDTEMPCHAMBER
+//#define CHAMBER_LIMIT_SWITCHING
+
+/**
+ * Max Chamber Power
+ * Applies to all forms of chamber control (PID, bang-bang, and bang-bang with hysteresis).
+ * When set to any value below 255, enables a form of PWM to the chamber heater that acts like a divider
+ * so don't use it unless you are OK with PWM on your heater. (See the comment on enabling PIDTEMPCHAMBER)
+ */
+#define MAX_CHAMBER_POWER 255 // limits duty cycle to chamber heater; 255=full current
+
+#if ENABLED(PIDTEMPCHAMBER)
+ #define MIN_CHAMBER_POWER 0
+ //#define PID_CHAMBER_DEBUG // Print Chamber PID debug data to the serial port.
+
+ // Lasko "MyHeat Personal Heater" (200w) modified with a Fotek SSR-10DA to control only the heating element
+ // and placed inside the small Creality printer enclosure tent.
+ //
+ #define DEFAULT_chamberKp 37.04
+ #define DEFAULT_chamberKi 1.40
+ #define DEFAULT_chamberKd 655.17
+ // M309 P37.04 I1.04 D655.17
+
+ // FIND YOUR OWN: "M303 E-2 C8 S50" to run autotune on the chamber at 50 degreesC for 8 cycles.
+#endif // PIDTEMPCHAMBER
+
+#if ANY(PIDTEMP, PIDTEMPBED, PIDTEMPCHAMBER)
+ //#define PID_OPENLOOP // Puts PID in open loop. M104/M140 sets the output power from 0 to PID_MAX
+ //#define SLOW_PWM_HEATERS // PWM with very low frequency (roughly 0.125Hz=8s) and minimum state time of approximately 1s useful for heaters driven by a relay
+ #define PID_FUNCTIONAL_RANGE 10 // If the temperature difference between the target temperature and the actual temperature
+ // is more than PID_FUNCTIONAL_RANGE then the PID will be shut off and the heater will be set to min/max.
+
+ //#define PID_EDIT_MENU // Add PID editing to the "Advanced Settings" menu. (~700 bytes of flash)
+ //#define PID_AUTOTUNE_MENU // Add PID auto-tuning to the "Advanced Settings" menu. (~250 bytes of flash)
+#endif
+
+// @section safety
+
+/**
+ * Prevent extrusion if the temperature is below EXTRUDE_MINTEMP.
+ * Add M302 to set the minimum extrusion temperature and/or turn
+ * cold extrusion prevention on and off.
+ *
+ * *** IT IS HIGHLY RECOMMENDED TO LEAVE THIS OPTION ENABLED! ***
+ */
+#define PREVENT_COLD_EXTRUSION
+#define EXTRUDE_MINTEMP 180 // MRiscoC Customizable by menu
+
+/**
+ * Prevent a single extrusion longer than EXTRUDE_MAXLENGTH.
+ * Note: For Bowden Extruders make this large enough to allow load/unload.
+ */
+#define PREVENT_LENGTHY_EXTRUDE
+#define EXTRUDE_MAXLENGTH 1000 // Ender Configs
+
+//===========================================================================
+//======================== Thermal Runaway Protection =======================
+//===========================================================================
+
+/**
+ * Thermal Protection provides additional protection to your printer from damage
+ * and fire. Marlin always includes safe min and max temperature ranges which
+ * protect against a broken or disconnected thermistor wire.
+ *
+ * The issue: If a thermistor falls out, it will report the much lower
+ * temperature of the air in the room, and the the firmware will keep
+ * the heater on.
+ *
+ * If you get "Thermal Runaway" or "Heating failed" errors the
+ * details can be tuned in Configuration_adv.h
+ */
+
+#define THERMAL_PROTECTION_HOTENDS // Enable thermal protection for all extruders
+#define THERMAL_PROTECTION_BED // Enable thermal protection for the heated bed
+//#define THERMAL_PROTECTION_CHAMBER // Enable thermal protection for the heated chamber
+//#define THERMAL_PROTECTION_COOLER // Enable thermal protection for the laser cooling
+
+//===========================================================================
+//============================= Mechanical Settings =========================
+//===========================================================================
+
+// @section machine
+
+// Enable one of the options below for CoreXY, CoreXZ, or CoreYZ kinematics,
+// either in the usual order or reversed
+//#define COREXY
+//#define COREXZ
+//#define COREYZ
+//#define COREYX
+//#define COREZX
+//#define COREZY
+//#define MARKFORGED_XY // MarkForged. See https://reprap.org/forum/read.php?152,504042
+//#define MARKFORGED_YX
+
+// Enable for a belt style printer with endless "Z" motion
+//#define BELTPRINTER
+
+// Enable for Polargraph Kinematics
+//#define POLARGRAPH
+#if ENABLED(POLARGRAPH)
+ #define POLARGRAPH_MAX_BELT_LEN 1035.0 // (mm) Belt length at full extension. Override with M665 H.
+ #define DEFAULT_SEGMENTS_PER_SECOND 5 // Move segmentation based on duration
+ #define PEN_UP_DOWN_MENU // Add "Pen Up" and "Pen Down" to the MarlinUI menu
+#endif
+
+// @section delta
+
+// Enable for DELTA kinematics and configure below
+//#define DELTA
+#if ENABLED(DELTA)
+
+ // Make delta curves from many straight lines (linear interpolation).
+ // This is a trade-off between visible corners (not enough segments)
+ // and processor overload (too many expensive sqrt calls).
+ #define DEFAULT_SEGMENTS_PER_SECOND 200
+
+ // After homing move down to a height where XY movement is unconstrained
+ //#define DELTA_HOME_TO_SAFE_ZONE
+
+ // Delta calibration menu
+ // Add three-point calibration to the MarlinUI menu.
+ // See http://minow.blogspot.com/index.html#4918805519571907051
+ //#define DELTA_CALIBRATION_MENU
+
+ // G33 Delta Auto-Calibration. Enable EEPROM_SETTINGS to store results.
+ //#define DELTA_AUTO_CALIBRATION
+
+ #if ENABLED(DELTA_AUTO_CALIBRATION)
+ // Default number of probe points : n*n (1 -> 7)
+ #define DELTA_CALIBRATION_DEFAULT_POINTS 4
+ #endif
+
+ #if EITHER(DELTA_AUTO_CALIBRATION, DELTA_CALIBRATION_MENU)
+ // Step size for paper-test probing
+ #define PROBE_MANUALLY_STEP 0.05 // (mm)
+ #endif
+
+ // Print surface diameter/2 minus unreachable space (avoid collisions with vertical towers).
+ #define PRINTABLE_RADIUS 140.0 // (mm)
+
+ // Maximum reachable area
+ #define DELTA_MAX_RADIUS 140.0 // (mm)
+
+ // Center-to-center distance of the holes in the diagonal push rods.
+ #define DELTA_DIAGONAL_ROD 250.0 // (mm)
+
+ // Distance between bed and nozzle Z home position
+ #define DELTA_HEIGHT 250.00 // (mm) Get this value from G33 auto calibrate
+
+ #define DELTA_ENDSTOP_ADJ { 0.0, 0.0, 0.0 } // Get these values from G33 auto calibrate
+
+ // Horizontal distance bridged by diagonal push rods when effector is centered.
+ #define DELTA_RADIUS 124.0 // (mm) Get this value from G33 auto calibrate
+
+ // Trim adjustments for individual towers
+ // tower angle corrections for X and Y tower / rotate XYZ so Z tower angle = 0
+ // measured in degrees anticlockwise looking from above the printer
+ #define DELTA_TOWER_ANGLE_TRIM { 0.0, 0.0, 0.0 } // Get these values from G33 auto calibrate
+
+ // Delta radius and diagonal rod adjustments (mm)
+ //#define DELTA_RADIUS_TRIM_TOWER { 0.0, 0.0, 0.0 }
+ //#define DELTA_DIAGONAL_ROD_TRIM_TOWER { 0.0, 0.0, 0.0 }
+#endif
+
+// @section scara
+
+/**
+ * MORGAN_SCARA was developed by QHARLEY in South Africa in 2012-2013.
+ * Implemented and slightly reworked by JCERNY in June, 2014.
+ *
+ * Mostly Printed SCARA is an open source design by Tyler Williams. See:
+ * https://www.thingiverse.com/thing:2487048
+ * https://www.thingiverse.com/thing:1241491
+ */
+//#define MORGAN_SCARA
+//#define MP_SCARA
+#if EITHER(MORGAN_SCARA, MP_SCARA)
+ // If movement is choppy try lowering this value
+ #define DEFAULT_SEGMENTS_PER_SECOND 200
+
+ // Length of inner and outer support arms. Measure arm lengths precisely.
+ #define SCARA_LINKAGE_1 150 // (mm)
+ #define SCARA_LINKAGE_2 150 // (mm)
+
+ // SCARA tower offset (position of Tower relative to bed zero position)
+ // This needs to be reasonably accurate as it defines the printbed position in the SCARA space.
+ #define SCARA_OFFSET_X 100 // (mm)
+ #define SCARA_OFFSET_Y -56 // (mm)
+
+ #if ENABLED(MORGAN_SCARA)
+
+ //#define DEBUG_SCARA_KINEMATICS
+ #define FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly
+
+ // Radius around the center where the arm cannot reach
+ #define MIDDLE_DEAD_ZONE_R 0 // (mm)
+
+ #define THETA_HOMING_OFFSET 0 // Calculated from Calibration Guide and M360 / M114. See http://reprap.harleystudio.co.za/?page_id=1073
+ #define PSI_HOMING_OFFSET 0 // Calculated from Calibration Guide and M364 / M114. See http://reprap.harleystudio.co.za/?page_id=1073
+
+ #elif ENABLED(MP_SCARA)
+
+ #define SCARA_OFFSET_THETA1 12 // degrees
+ #define SCARA_OFFSET_THETA2 131 // degrees
+
+ #endif
+
+#endif
+
+// @section tpara
+
+// Enable for TPARA kinematics and configure below
+//#define AXEL_TPARA
+#if ENABLED(AXEL_TPARA)
+ #define DEBUG_TPARA_KINEMATICS
+ #define DEFAULT_SEGMENTS_PER_SECOND 200
+
+ // Length of inner and outer support arms. Measure arm lengths precisely.
+ #define TPARA_LINKAGE_1 120 // (mm)
+ #define TPARA_LINKAGE_2 120 // (mm)
+
+ // SCARA tower offset (position of Tower relative to bed zero position)
+ // This needs to be reasonably accurate as it defines the printbed position in the SCARA space.
+ #define TPARA_OFFSET_X 0 // (mm)
+ #define TPARA_OFFSET_Y 0 // (mm)
+ #define TPARA_OFFSET_Z 0 // (mm)
+
+ #define FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly
+
+ // Radius around the center where the arm cannot reach
+ #define MIDDLE_DEAD_ZONE_R 0 // (mm)
+
+ // Calculated from Calibration Guide and M360 / M114. See http://reprap.harleystudio.co.za/?page_id=1073
+ #define THETA_HOMING_OFFSET 0
+ #define PSI_HOMING_OFFSET 0
+#endif
+
+// @section polar
+
+/**
+ * POLAR Kinematics
+ * developed by Kadir ilkimen for PolarBear CNC and babyBear
+ * https://github.com/kadirilkimen/Polar-Bear-Cnc-Machine
+ * https://github.com/kadirilkimen/babyBear-3D-printer
+ *
+ * A polar machine can have different configurations.
+ * This kinematics is only compatible with the following configuration:
+ * X : Independent linear
+ * Y or B : Polar
+ * Z : Independent linear
+ *
+ * For example, PolarBear has CoreXZ plus Polar Y or B.
+ *
+ * Motion problem for Polar axis near center / origin:
+ *
+ * 3D printing:
+ * Movements very close to the center of the polar axis take more time than others.
+ * This brief delay results in more material deposition due to the pressure in the nozzle.
+ *
+ * Current Kinematics and feedrate scaling deals with this by making the movement as fast
+ * as possible. It works for slow movements but doesn't work well with fast ones. A more
+ * complicated extrusion compensation must be implemented.
+ *
+ * Ideally, it should estimate that a long rotation near the center is ahead and will cause
+ * unwanted deposition. Therefore it can compensate the extrusion beforehand.
+ *
+ * Laser cutting:
+ * Same thing would be a problem for laser engraving too. As it spends time rotating at the
+ * center point, more likely it will burn more material than it should. Therefore similar
+ * compensation would be implemented for laser-cutting operations.
+ *
+ * Milling:
+ * This shouldn't be a problem for cutting/milling operations.
+ */
+//#define POLAR
+#if ENABLED(POLAR)
+ #define DEFAULT_SEGMENTS_PER_SECOND 180 // If movement is choppy try lowering this value
+ #define PRINTABLE_RADIUS 82.0f // (mm) Maximum travel of X axis
+
+ // Movements fall inside POLAR_FAST_RADIUS are assigned the highest possible feedrate
+ // to compensate unwanted deposition related to the near-origin motion problem.
+ #define POLAR_FAST_RADIUS 3.0f // (mm)
+
+ // Radius which is unreachable by the tool.
+ // Needed if the tool is not perfectly aligned to the center of the polar axis.
+ #define POLAR_CENTER_OFFSET 0.0f // (mm)
+
+ #define FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly
+#endif
+
+// @section machine
+
+// Articulated robot (arm). Joints are directly mapped to axes with no kinematics.
+//#define ARTICULATED_ROBOT_ARM
+
+// For a hot wire cutter with parallel horizontal axes (X, I) where the heights of the two wire
+// ends are controlled by parallel axes (Y, J). Joints are directly mapped to axes (no kinematics).
+//#define FOAMCUTTER_XYUV
+
+//===========================================================================
+//============================== Endstop Settings ===========================
+//===========================================================================
+
+// @section endstops
+
+// Specify here all the endstop connectors that are connected to any endstop or probe.
+// Almost all printers will be using one per axis. Probes will use one or more of the
+// extra connectors. Leave undefined any used for non-endstop and non-probe purposes.
+#define USE_XMIN_PLUG
+#define USE_YMIN_PLUG
+#define USE_ZMIN_PLUG
+//#define USE_IMIN_PLUG
+//#define USE_JMIN_PLUG
+//#define USE_KMIN_PLUG
+//#define USE_UMIN_PLUG
+//#define USE_VMIN_PLUG
+//#define USE_WMIN_PLUG
+//#define USE_XMAX_PLUG
+//#define USE_YMAX_PLUG
+//#define USE_ZMAX_PLUG
+//#define USE_IMAX_PLUG
+//#define USE_JMAX_PLUG
+//#define USE_KMAX_PLUG
+//#define USE_UMAX_PLUG
+//#define USE_VMAX_PLUG
+//#define USE_WMAX_PLUG
+
+// Enable pullup for all endstops to prevent a floating state
+#define ENDSTOPPULLUPS
+#if DISABLED(ENDSTOPPULLUPS)
+ // Disable ENDSTOPPULLUPS to set pullups individually
+ //#define ENDSTOPPULLUP_XMIN
+ //#define ENDSTOPPULLUP_YMIN
+ //#define ENDSTOPPULLUP_ZMIN
+ //#define ENDSTOPPULLUP_IMIN
+ //#define ENDSTOPPULLUP_JMIN
+ //#define ENDSTOPPULLUP_KMIN
+ //#define ENDSTOPPULLUP_UMIN
+ //#define ENDSTOPPULLUP_VMIN
+ //#define ENDSTOPPULLUP_WMIN
+ //#define ENDSTOPPULLUP_XMAX
+ //#define ENDSTOPPULLUP_YMAX
+ //#define ENDSTOPPULLUP_ZMAX
+ //#define ENDSTOPPULLUP_IMAX
+ //#define ENDSTOPPULLUP_JMAX
+ //#define ENDSTOPPULLUP_KMAX
+ //#define ENDSTOPPULLUP_UMAX
+ //#define ENDSTOPPULLUP_VMAX
+ //#define ENDSTOPPULLUP_WMAX
+ //#define ENDSTOPPULLUP_ZMIN_PROBE
+#endif
+
+// Enable pulldown for all endstops to prevent a floating state
+//#define ENDSTOPPULLDOWNS
+#if DISABLED(ENDSTOPPULLDOWNS)
+ // Disable ENDSTOPPULLDOWNS to set pulldowns individually
+ //#define ENDSTOPPULLDOWN_XMIN
+ //#define ENDSTOPPULLDOWN_YMIN
+ //#define ENDSTOPPULLDOWN_ZMIN
+ //#define ENDSTOPPULLDOWN_IMIN
+ //#define ENDSTOPPULLDOWN_JMIN
+ //#define ENDSTOPPULLDOWN_KMIN
+ //#define ENDSTOPPULLDOWN_UMIN
+ //#define ENDSTOPPULLDOWN_VMIN
+ //#define ENDSTOPPULLDOWN_WMIN
+ //#define ENDSTOPPULLDOWN_XMAX
+ //#define ENDSTOPPULLDOWN_YMAX
+ //#define ENDSTOPPULLDOWN_ZMAX
+ //#define ENDSTOPPULLDOWN_IMAX
+ //#define ENDSTOPPULLDOWN_JMAX
+ //#define ENDSTOPPULLDOWN_KMAX
+ //#define ENDSTOPPULLDOWN_UMAX
+ //#define ENDSTOPPULLDOWN_VMAX
+ //#define ENDSTOPPULLDOWN_WMAX
+ //#define ENDSTOPPULLDOWN_ZMIN_PROBE
+#endif
+
+// Mechanical endstop with COM to ground and NC to Signal uses "false" here (most common setup).
+#define X_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define Y_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define Z_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define I_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define J_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define K_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define U_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define V_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define W_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define X_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define Y_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define Z_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define I_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define J_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define K_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define U_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define V_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define W_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define Z_MIN_PROBE_ENDSTOP_INVERTING false // Set to true to invert the logic of the probe.
+
+// Enable this feature if all enabled endstop pins are interrupt-capable.
+// This will remove the need to poll the interrupt pins, saving many CPU cycles.
+#define ENDSTOP_INTERRUPTS_FEATURE // Ender Configs
+
+/**
+ * Endstop Noise Threshold
+ *
+ * Enable if your probe or endstops falsely trigger due to noise.
+ *
+ * - Higher values may affect repeatability or accuracy of some bed probes.
+ * - To fix noise install a 100nF ceramic capacitor in parallel with the switch.
+ * - This feature is not required for common micro-switches mounted on PCBs
+ * based on the Makerbot design, which already have the 100nF capacitor.
+ *
+ * :[2,3,4,5,6,7]
+ */
+//#define ENDSTOP_NOISE_THRESHOLD 2
+
+// Check for stuck or disconnected endstops during homing moves.
+//#define DETECT_BROKEN_ENDSTOP
+
+//=============================================================================
+//============================== Movement Settings ============================
+//=============================================================================
+// @section motion
+
+/**
+ * Default Settings
+ *
+ * These settings can be reset by M502
+ *
+ * Note that if EEPROM is enabled, saved values will override these.
+ */
+
+/**
+ * With this option each E stepper can have its own factors for the
+ * following movement settings. If fewer factors are given than the
+ * total number of extruders, the last value applies to the rest.
+ */
+//#define DISTINCT_E_FACTORS
+
+/**
+ * Default Axis Steps Per Unit (linear=steps/mm, rotational=steps/°)
+ * Override with M92
+ * X, Y, Z [, I [, J [, K...]]], E0 [, E1[, E2...]]
+ */
+#define DEFAULT_AXIS_STEPS_PER_UNIT { 80, 80, 400, 93 } // Ender Configs
+
+/**
+ * Default Max Feed Rate (linear=mm/s, rotational=°/s)
+ * Override with M203
+ * X, Y, Z [, I [, J [, K...]]], E0 [, E1[, E2...]]
+ */
+#define DEFAULT_MAX_FEEDRATE { 300, 300, 25, 60 } // Ender Configs
+
+#define LIMITED_MAX_FR_EDITING // Limit edit via M203 or LCD to DEFAULT_MAX_FEEDRATE * 2 // MRiscoC allows higher limits
+#if ENABLED(LIMITED_MAX_FR_EDITING)
+ #define MAX_FEEDRATE_EDIT_VALUES { 1000, 1000, 40, 200 } // ...or, set your own edit limits // MRiscoC allows higher limits
+#endif
+
+/**
+ * Default Max Acceleration (speed change with time) (linear=mm/(s^2), rotational=°/(s^2))
+ * (Maximum start speed for accelerated moves)
+ * Override with M201
+ * X, Y, Z [, I [, J [, K...]]], E0 [, E1[, E2...]]
+ */
+#define DEFAULT_MAX_ACCELERATION { 500, 500, 100, 1000 } // Ender Configs
+
+#define LIMITED_MAX_ACCEL_EDITING // Limit edit via M201 or LCD to DEFAULT_MAX_ACCELERATION * 2 // MRiscoC allows higher limits
+#if ENABLED(LIMITED_MAX_ACCEL_EDITING)
+ #define MAX_ACCEL_EDIT_VALUES { 9999, 9999, 999, 9999 } // ...or, set your own edit limits // MRiscoC allows higher limits
+#endif
+
+/**
+ * Default Acceleration (speed change with time) (linear=mm/(s^2), rotational=°/(s^2))
+ * Override with M204
+ *
+ * M204 P Acceleration
+ * M204 R Retract Acceleration
+ * M204 T Travel Acceleration
+ */
+#define DEFAULT_ACCELERATION 500 // X, Y, Z and E acceleration for printing moves // Ender Configs
+#define DEFAULT_RETRACT_ACCELERATION 800 // E acceleration for retracts // Ender Configs
+#define DEFAULT_TRAVEL_ACCELERATION 1000 // X, Y, Z acceleration for travel (non printing) moves // Ender Configs
+
+/**
+ * Default Jerk limits (mm/s)
+ * Override with M205 X Y Z . . . E
+ *
+ * "Jerk" specifies the minimum speed change that requires acceleration.
+ * When changing speed and direction, if the difference is less than the
+ * value set here, it may happen instantaneously.
+ */
+#define CLASSIC_JERK // Ender Configs
+#if ENABLED(CLASSIC_JERK)
+ #define DEFAULT_XJERK 8.0 // Ender Configs
+ #define DEFAULT_YJERK 8.0 // Ender Configs
+ #define DEFAULT_ZJERK 0.4 // Ender Configs
+ //#define DEFAULT_IJERK 0.3
+ //#define DEFAULT_JJERK 0.3
+ //#define DEFAULT_KJERK 0.3
+ //#define DEFAULT_UJERK 0.3
+ //#define DEFAULT_VJERK 0.3
+ //#define DEFAULT_WJERK 0.3
+
+ //#define TRAVEL_EXTRA_XYJERK 0.0 // Additional jerk allowance for all travel moves
+
+ #define LIMITED_JERK_EDITING // Limit edit via M205 or LCD to DEFAULT_aJERK * 2 // Ender Configs
+ #if ENABLED(LIMITED_JERK_EDITING)
+ #define MAX_JERK_EDIT_VALUES { 20, 20, 1, 20 } // ...or, set your own edit limits // MRiscoC allows higher limits
+ #endif
+#endif
+
+#define DEFAULT_EJERK 5.0 // May be used by Linear Advance // Ender Configs
+
+/**
+ * Junction Deviation Factor
+ *
+ * See:
+ * https://reprap.org/forum/read.php?1,739819
+ * https://blog.kyneticcnc.com/2018/10/computing-junction-deviation-for-marlin.html
+ */
+#if DISABLED(CLASSIC_JERK)
+ #define JUNCTION_DEVIATION_MM 0.013 // (mm) Distance from real junction edge
+ #define JD_HANDLE_SMALL_SEGMENTS // Use curvature estimation instead of just the junction angle
+ // for small segments (< 1mm) with large junction angles (> 135°).
+#endif
+
+/**
+ * S-Curve Acceleration
+ *
+ * This option eliminates vibration during printing by fitting a Bézier
+ * curve to move acceleration, producing much smoother direction changes.
+ *
+ * See https://github.com/synthetos/TinyG/wiki/Jerk-Controlled-Motion-Explained
+ */
+#define S_CURVE_ACCELERATION // MRiscoC Enabled
+
+//===========================================================================
+//============================= Z Probe Options =============================
+//===========================================================================
+// @section probes
+
+//
+// See https://marlinfw.org/docs/configuration/probes.html
+//
+
+/**
+ * Enable this option for a probe connected to the Z-MIN pin.
+ * The probe replaces the Z-MIN endstop and is used for Z homing.
+ * (Automatically enables USE_PROBE_FOR_Z_HOMING.)
+ */
+#define Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN
+
+// Force the use of the probe for Z-axis homing
+//#define USE_PROBE_FOR_Z_HOMING // Manual mesh not have a probe
+
+/**
+ * Z_MIN_PROBE_PIN
+ *
+ * Define this pin if the probe is not connected to Z_MIN_PIN.
+ * If not defined the default pin for the selected MOTHERBOARD
+ * will be used. Most of the time the default is what you want.
+ *
+ * - The simplest option is to use a free endstop connector.
+ * - Use 5V for powered (usually inductive) sensors.
+ *
+ * - RAMPS 1.3/1.4 boards may use the 5V, GND, and Aux4->D32 pin:
+ * - For simple switches connect...
+ * - normally-closed switches to GND and D32.
+ * - normally-open switches to 5V and D32.
+ */
+//#define Z_MIN_PROBE_PIN 32 // Pin 32 is the RAMPS default
+
+/**
+ * Probe Type
+ *
+ * Allen Key Probes, Servo Probes, Z-Sled Probes, FIX_MOUNTED_PROBE, etc.
+ * Activate one of these to use Auto Bed Leveling below.
+ */
+
+/**
+ * The "Manual Probe" provides a means to do "Auto" Bed Leveling without a probe.
+ * Use G29 repeatedly, adjusting the Z height at each point with movement commands
+ * or (with LCD_BED_LEVELING) the LCD controller.
+ */
+#define PROBE_MANUALLY // Manual mesh version
+
+/**
+ * A Fix-Mounted Probe either doesn't deploy or needs manual deployment.
+ * (e.g., an inductive probe or a nozzle-based probe-switch.)
+ */
+//#define FIX_MOUNTED_PROBE
+
+/**
+ * Use the nozzle as the probe, as with a conductive
+ * nozzle system or a piezo-electric smart effector.
+ */
+//#define NOZZLE_AS_PROBE
+
+/**
+ * Z Servo Probe, such as an endstop switch on a rotating arm.
+ */
+//#define Z_PROBE_SERVO_NR 0 // Defaults to SERVO 0 connector.
+//#define Z_SERVO_ANGLES { 70, 0 } // Z Servo Deploy and Stow angles
+
+/**
+ * The BLTouch probe uses a Hall effect sensor and emulates a servo.
+ */
+//#define BLTOUCH
+
+/**
+ * MagLev V4 probe by MDD
+ *
+ * This probe is deployed and activated by powering a built-in electromagnet.
+ */
+//#define MAGLEV4
+#if ENABLED(MAGLEV4)
+ //#define MAGLEV_TRIGGER_PIN 11 // Set to the connected digital output
+ #define MAGLEV_TRIGGER_DELAY 15 // Changing this risks overheating the coil
+#endif
+
+/**
+ * Touch-MI Probe by hotends.fr
+ *
+ * This probe is deployed and activated by moving the X-axis to a magnet at the edge of the bed.
+ * By default, the magnet is assumed to be on the left and activated by a home. If the magnet is
+ * on the right, enable and set TOUCH_MI_DEPLOY_XPOS to the deploy position.
+ *
+ * Also requires: BABYSTEPPING, BABYSTEP_ZPROBE_OFFSET, Z_SAFE_HOMING,
+ * and a minimum Z_HOMING_HEIGHT of 10.
+ */
+//#define TOUCH_MI_PROBE
+#if ENABLED(TOUCH_MI_PROBE)
+ #define TOUCH_MI_RETRACT_Z 0.5 // Height at which the probe retracts
+ //#define TOUCH_MI_DEPLOY_XPOS (X_MAX_BED + 2) // For a magnet on the right side of the bed
+ //#define TOUCH_MI_MANUAL_DEPLOY // For manual deploy (LCD menu)
+#endif
+
+// A probe that is deployed and stowed with a solenoid pin (SOL1_PIN)
+//#define SOLENOID_PROBE
+
+// A sled-mounted probe like those designed by Charles Bell.
+//#define Z_PROBE_SLED
+//#define SLED_DOCKING_OFFSET 5 // The extra distance the X axis must travel to pickup the sled. 0 should be fine but you can push it further if you'd like.
+
+// A probe deployed by moving the x-axis, such as the Wilson II's rack-and-pinion probe designed by Marty Rice.
+//#define RACK_AND_PINION_PROBE
+#if ENABLED(RACK_AND_PINION_PROBE)
+ #define Z_PROBE_DEPLOY_X X_MIN_POS
+ #define Z_PROBE_RETRACT_X X_MAX_POS
+#endif
+
+/**
+ * Magnetically Mounted Probe
+ * For probes such as Euclid, Klicky, Klackender, etc.
+ */
+//#define MAG_MOUNTED_PROBE
+#if ENABLED(MAG_MOUNTED_PROBE)
+ #define PROBE_DEPLOY_FEEDRATE (133*60) // (mm/min) Probe deploy speed
+ #define PROBE_STOW_FEEDRATE (133*60) // (mm/min) Probe stow speed
+
+ #define MAG_MOUNTED_DEPLOY_1 { PROBE_DEPLOY_FEEDRATE, { 245, 114, 30 } } // Move to side Dock & Attach probe
+ #define MAG_MOUNTED_DEPLOY_2 { PROBE_DEPLOY_FEEDRATE, { 210, 114, 30 } } // Move probe off dock
+ #define MAG_MOUNTED_DEPLOY_3 { PROBE_DEPLOY_FEEDRATE, { 0, 0, 0 } } // Extra move if needed
+ #define MAG_MOUNTED_DEPLOY_4 { PROBE_DEPLOY_FEEDRATE, { 0, 0, 0 } } // Extra move if needed
+ #define MAG_MOUNTED_DEPLOY_5 { PROBE_DEPLOY_FEEDRATE, { 0, 0, 0 } } // Extra move if needed
+ #define MAG_MOUNTED_STOW_1 { PROBE_STOW_FEEDRATE, { 245, 114, 20 } } // Move to dock
+ #define MAG_MOUNTED_STOW_2 { PROBE_STOW_FEEDRATE, { 245, 114, 0 } } // Place probe beside remover
+ #define MAG_MOUNTED_STOW_3 { PROBE_STOW_FEEDRATE, { 230, 114, 0 } } // Side move to remove probe
+ #define MAG_MOUNTED_STOW_4 { PROBE_STOW_FEEDRATE, { 210, 114, 20 } } // Side move to remove probe
+ #define MAG_MOUNTED_STOW_5 { PROBE_STOW_FEEDRATE, { 0, 0, 0 } } // Extra move if needed
+#endif
+
+// Duet Smart Effector (for delta printers) - https://bit.ly/2ul5U7J
+// When the pin is defined you can use M672 to set/reset the probe sensitivity.
+//#define DUET_SMART_EFFECTOR
+#if ENABLED(DUET_SMART_EFFECTOR)
+ #define SMART_EFFECTOR_MOD_PIN -1 // Connect a GPIO pin to the Smart Effector MOD pin
+#endif
+
+/**
+ * Use StallGuard2 to probe the bed with the nozzle.
+ * Requires stallGuard-capable Trinamic stepper drivers.
+ * CAUTION: This can damage machines with Z lead screws.
+ * Take extreme care when setting up this feature.
+ */
+//#define SENSORLESS_PROBING
+
+/**
+ * Allen key retractable z-probe as seen on many Kossel delta printers - https://reprap.org/wiki/Kossel#Automatic_bed_leveling_probe
+ * Deploys by touching z-axis belt. Retracts by pushing the probe down.
+ */
+//#define Z_PROBE_ALLEN_KEY
+#if ENABLED(Z_PROBE_ALLEN_KEY)
+ // 2 or 3 sets of coordinates for deploying and retracting the spring loaded touch probe on G29,
+ // if servo actuated touch probe is not defined. Uncomment as appropriate for your printer/probe.
+
+ #define Z_PROBE_ALLEN_KEY_DEPLOY_1 { 30.0, PRINTABLE_RADIUS, 100.0 }
+ #define Z_PROBE_ALLEN_KEY_DEPLOY_1_FEEDRATE XY_PROBE_FEEDRATE
+
+ #define Z_PROBE_ALLEN_KEY_DEPLOY_2 { 0.0, PRINTABLE_RADIUS, 100.0 }
+ #define Z_PROBE_ALLEN_KEY_DEPLOY_2_FEEDRATE (XY_PROBE_FEEDRATE)/10
+
+ #define Z_PROBE_ALLEN_KEY_DEPLOY_3 { 0.0, (PRINTABLE_RADIUS) * 0.75, 100.0 }
+ #define Z_PROBE_ALLEN_KEY_DEPLOY_3_FEEDRATE XY_PROBE_FEEDRATE
+
+ #define Z_PROBE_ALLEN_KEY_STOW_1 { -64.0, 56.0, 23.0 } // Move the probe into position
+ #define Z_PROBE_ALLEN_KEY_STOW_1_FEEDRATE XY_PROBE_FEEDRATE
+
+ #define Z_PROBE_ALLEN_KEY_STOW_2 { -64.0, 56.0, 3.0 } // Push it down
+ #define Z_PROBE_ALLEN_KEY_STOW_2_FEEDRATE (XY_PROBE_FEEDRATE)/10
+
+ #define Z_PROBE_ALLEN_KEY_STOW_3 { -64.0, 56.0, 50.0 } // Move it up to clear
+ #define Z_PROBE_ALLEN_KEY_STOW_3_FEEDRATE XY_PROBE_FEEDRATE
+
+ #define Z_PROBE_ALLEN_KEY_STOW_4 { 0.0, 0.0, 50.0 }
+ #define Z_PROBE_ALLEN_KEY_STOW_4_FEEDRATE XY_PROBE_FEEDRATE
+
+#endif // Z_PROBE_ALLEN_KEY
+
+/**
+ * Nozzle-to-Probe offsets { X, Y, Z }
+ *
+ * X and Y offset
+ * Use a caliper or ruler to measure the distance from the tip of
+ * the Nozzle to the center-point of the Probe in the X and Y axes.
+ *
+ * Z offset
+ * - For the Z offset use your best known value and adjust at runtime.
+ * - Common probes trigger below the nozzle and have negative values for Z offset.
+ * - Probes triggering above the nozzle height are uncommon but do exist. When using
+ * probes such as this, carefully set Z_CLEARANCE_DEPLOY_PROBE and Z_CLEARANCE_BETWEEN_PROBES
+ * to avoid collisions during probing.
+ *
+ * Tune and Adjust
+ * - Probe Offsets can be tuned at runtime with 'M851', LCD menus, babystepping, etc.
+ * - PROBE_OFFSET_WIZARD (configuration_adv.h) can be used for setting the Z offset.
+ *
+ * Assuming the typical work area orientation:
+ * - Probe to RIGHT of the Nozzle has a Positive X offset
+ * - Probe to LEFT of the Nozzle has a Negative X offset
+ * - Probe in BACK of the Nozzle has a Positive Y offset
+ * - Probe in FRONT of the Nozzle has a Negative Y offset
+ *
+ * Some examples:
+ * #define NOZZLE_TO_PROBE_OFFSET { 10, 10, -1 } // Example "1"
+ * #define NOZZLE_TO_PROBE_OFFSET {-10, 5, -1 } // Example "2"
+ * #define NOZZLE_TO_PROBE_OFFSET { 5, -5, -1 } // Example "3"
+ * #define NOZZLE_TO_PROBE_OFFSET {-15,-10, -1 } // Example "4"
+ *
+ * +-- BACK ---+
+ * | [+] |
+ * L | 1 | R <-- Example "1" (right+, back+)
+ * E | 2 | I <-- Example "2" ( left-, back+)
+ * F |[-] N [+]| G <-- Nozzle
+ * T | 3 | H <-- Example "3" (right+, front-)
+ * | 4 | T <-- Example "4" ( left-, front-)
+ * | [-] |
+ * O-- FRONT --+
+ */
+#define NOZZLE_TO_PROBE_OFFSET { 0, 0, 0 } // MRiscoC BLTouch offset for support: https://www.thingiverse.com/thing:4605354 (z-offset = -1.80 mm) // Manual mesh use the nozzle as probe
+
+// Most probes should stay away from the edges of the bed, but
+// with NOZZLE_AS_PROBE this can be negative for a wider probing area.
+#define PROBING_MARGIN 10 // MRiscoC Avoid clips on borders
+
+// X and Y axis travel speed (mm/min) between probes
+#define XY_PROBE_FEEDRATE (200*60) // MRiscoC increase travel speed between probes
+
+// Feedrate (mm/min) for the first approach when double-probing (MULTIPLE_PROBING == 2)
+#define Z_PROBE_FEEDRATE_FAST (16*60) // MRiscoC increase probe Z speed
+
+// Feedrate (mm/min) for the "accurate" probe of each point
+#define Z_PROBE_FEEDRATE_SLOW (Z_PROBE_FEEDRATE_FAST / 2)
+
+/**
+ * Probe Activation Switch
+ * A switch indicating proper deployment, or an optical
+ * switch triggered when the carriage is near the bed.
+ */
+//#define PROBE_ACTIVATION_SWITCH
+#if ENABLED(PROBE_ACTIVATION_SWITCH)
+ #define PROBE_ACTIVATION_SWITCH_STATE LOW // State indicating probe is active
+ //#define PROBE_ACTIVATION_SWITCH_PIN PC6 // Override default pin
+#endif
+
+/**
+ * Tare Probe (determine zero-point) prior to each probe.
+ * Useful for a strain gauge or piezo sensor that needs to factor out
+ * elements such as cables pulling on the carriage.
+ */
+//#define PROBE_TARE
+#if ENABLED(PROBE_TARE)
+ #define PROBE_TARE_TIME 200 // (ms) Time to hold tare pin
+ #define PROBE_TARE_DELAY 200 // (ms) Delay after tare before
+ #define PROBE_TARE_STATE HIGH // State to write pin for tare
+ //#define PROBE_TARE_PIN PA5 // Override default pin
+ #if ENABLED(PROBE_ACTIVATION_SWITCH)
+ //#define PROBE_TARE_ONLY_WHILE_INACTIVE // Fail to tare/probe if PROBE_ACTIVATION_SWITCH is active
+ #endif
+#endif
+
+/**
+ * Probe Enable / Disable
+ * The probe only provides a triggered signal when enabled.
+ */
+//#define PROBE_ENABLE_DISABLE
+#if ENABLED(PROBE_ENABLE_DISABLE)
+ //#define PROBE_ENABLE_PIN -1 // Override the default pin here
+#endif
+
+/**
+ * Multiple Probing
+ *
+ * You may get improved results by probing 2 or more times.
+ * With EXTRA_PROBING the more atypical reading(s) will be disregarded.
+ *
+ * A total of 2 does fast/slow probes with a weighted average.
+ * A total of 3 or more adds more slow probes, taking the average.
+ */
+//#define MULTIPLE_PROBING 2
+//#define EXTRA_PROBING 1
+
+/**
+ * Z probes require clearance when deploying, stowing, and moving between
+ * probe points to avoid hitting the bed and other hardware.
+ * Servo-mounted probes require extra space for the arm to rotate.
+ * Inductive probes need space to keep from triggering early.
+ *
+ * Use these settings to specify the distance (mm) to raise the probe (or
+ * lower the bed). The values set here apply over and above any (negative)
+ * probe Z Offset set with NOZZLE_TO_PROBE_OFFSET, M851, or the LCD.
+ * Only integer values >= 1 are valid here.
+ *
+ * Example: `M851 Z-5` with a CLEARANCE of 4 => 9mm from bed to nozzle.
+ * But: `M851 Z+1` with a CLEARANCE of 2 => 2mm from bed to nozzle.
+ */
+#define Z_CLEARANCE_DEPLOY_PROBE 10 // Z Clearance for Deploy/Stow
+#define Z_CLEARANCE_BETWEEN_PROBES 5 // Z Clearance between probe points
+#define Z_CLEARANCE_MULTI_PROBE 5 // Z Clearance between multiple probes
+//#define Z_AFTER_PROBING 5 // Z position after probing is done
+
+#define Z_PROBE_LOW_POINT -2 // Farthest distance below the trigger-point to go before stopping
+
+// For M851 give a range for adjusting the Z probe offset
+#define Z_PROBE_OFFSET_RANGE_MIN -20
+#define Z_PROBE_OFFSET_RANGE_MAX 20
+
+// Enable the M48 repeatability test to test probe accuracy
+//#define Z_MIN_PROBE_REPEATABILITY_TEST
+
+// Before deploy/stow pause for user confirmation
+//#define PAUSE_BEFORE_DEPLOY_STOW
+#if ENABLED(PAUSE_BEFORE_DEPLOY_STOW)
+ //#define PAUSE_PROBE_DEPLOY_WHEN_TRIGGERED // For Manual Deploy Allenkey Probe
+#endif
+
+/**
+ * Enable one or more of the following if probing seems unreliable.
+ * Heaters and/or fans can be disabled during probing to minimize electrical
+ * noise. A delay can also be added to allow noise and vibration to settle.
+ * These options are most useful for the BLTouch probe, but may also improve
+ * readings with inductive probes and piezo sensors.
+ */
+//#define PROBING_HEATERS_OFF // Turn heaters off when probing
+#if ENABLED(PROBING_HEATERS_OFF)
+ //#define WAIT_FOR_BED_HEATER // Wait for bed to heat back up between probes (to improve accuracy)
+ //#define WAIT_FOR_HOTEND // Wait for hotend to heat back up between probes (to improve accuracy & prevent cold extrude)
+#endif
+//#define PROBING_FANS_OFF // Turn fans off when probing
+//#define PROBING_ESTEPPERS_OFF // Turn all extruder steppers off when probing
+//#define PROBING_STEPPERS_OFF // Turn all steppers off (unless needed to hold position) when probing (including extruders)
+//#define DELAY_BEFORE_PROBING 200 // (ms) To prevent vibrations from triggering piezo sensors
+
+// Require minimum nozzle and/or bed temperature for probing
+//#define PREHEAT_BEFORE_PROBING
+#if ENABLED(PREHEAT_BEFORE_PROBING)
+ #define PROBING_NOZZLE_TEMP 120 // (°C) Only applies to E0 at this time
+ #define PROBING_BED_TEMP 50
+#endif
+
+// For Inverting Stepper Enable Pins (Active Low) use 0, Non Inverting (Active High) use 1
+// :{ 0:'Low', 1:'High' }
+#define X_ENABLE_ON 0
+#define Y_ENABLE_ON 0
+#define Z_ENABLE_ON 0
+#define E_ENABLE_ON 0 // For all extruders
+//#define I_ENABLE_ON 0
+//#define J_ENABLE_ON 0
+//#define K_ENABLE_ON 0
+//#define U_ENABLE_ON 0
+//#define V_ENABLE_ON 0
+//#define W_ENABLE_ON 0
+
+// Disable axis steppers immediately when they're not being stepped.
+// WARNING: When motors turn off there is a chance of losing position accuracy!
+//#define DISABLE_X
+//#define DISABLE_Y
+//#define DISABLE_Z
+//#define DISABLE_I
+//#define DISABLE_J
+//#define DISABLE_K
+//#define DISABLE_U
+//#define DISABLE_V
+//#define DISABLE_W
+
+// Turn off the display blinking that warns about possible accuracy reduction
+//#define DISABLE_REDUCED_ACCURACY_WARNING
+
+// @section extruder
+
+//#define DISABLE_E // Disable the extruder when not stepping
+#define DISABLE_INACTIVE_EXTRUDER // Keep only the active extruder enabled
+
+// @section motion
+
+// Invert the stepper direction. Change (or reverse the motor connector) if an axis goes the wrong way.
+#define INVERT_X_DIR false // Ender Configs
+#define INVERT_Y_DIR false // Ender Configs
+#define INVERT_Z_DIR true // Ender Configs
+//#define INVERT_I_DIR false
+//#define INVERT_J_DIR false
+//#define INVERT_K_DIR false
+//#define INVERT_U_DIR false
+//#define INVERT_V_DIR false
+//#define INVERT_W_DIR false
+
+// @section extruder
+
+// For direct drive extruder v9 set to true, for geared extruder set to false.
+#define INVERT_E0_DIR false
+#define INVERT_E1_DIR false
+#define INVERT_E2_DIR false
+#define INVERT_E3_DIR false
+#define INVERT_E4_DIR false
+#define INVERT_E5_DIR false
+#define INVERT_E6_DIR false
+#define INVERT_E7_DIR false
+
+// @section homing
+
+//#define NO_MOTION_BEFORE_HOMING // Inhibit movement until all axes have been homed. Also enable HOME_AFTER_DEACTIVATE for extra safety.
+#define HOME_AFTER_DEACTIVATE // Require rehoming after steppers are deactivated. Also enable NO_MOTION_BEFORE_HOMING for extra safety. // MRiscoC Enabled for safety
+
+/**
+ * Set Z_IDLE_HEIGHT if the Z-Axis moves on its own when steppers are disabled.
+ * - Use a low value (i.e., Z_MIN_POS) if the nozzle falls down to the bed.
+ * - Use a large value (i.e., Z_MAX_POS) if the bed falls down, away from the nozzle.
+ */
+//#define Z_IDLE_HEIGHT Z_HOME_POS
+
+//#define Z_HOMING_HEIGHT 10 // (mm) Minimal Z height before homing (G28) for Z clearance above the bed, clamps, ... // MRiscoC Crearance over the bed
+ // Be sure to have this much clearance over your Z_MAX_POS to prevent grinding.
+
+#define Z_AFTER_HOMING 5 // (mm) Height to move to after homing Z // MRiscoC Crearance over the bed
+
+// Direction of endstops when homing; 1=MAX, -1=MIN
+// :[-1,1]
+#define X_HOME_DIR -1
+#define Y_HOME_DIR -1
+#define Z_HOME_DIR -1
+//#define I_HOME_DIR -1
+//#define J_HOME_DIR -1
+//#define K_HOME_DIR -1
+//#define U_HOME_DIR -1
+//#define V_HOME_DIR -1
+//#define W_HOME_DIR -1
+
+// @section geometry
+
+// The size of the printable area
+#define X_BED_SIZE 230 // MRiscoC Max usable bed size
+#define Y_BED_SIZE 230 // MRiscoC Max usable bed size
+
+// Travel limits (linear=mm, rotational=°) after homing, corresponding to endstop positions.
+#define X_MIN_POS 0 // MRiscoC Stock physical limit
+#define Y_MIN_POS 0 // MRiscoC Stock physical limit
+#define Z_MIN_POS 0
+#define X_MAX_POS 248 // MRiscoC Stock physical limit
+#define Y_MAX_POS 231 // MRiscoC Stock physical limit
+#define Z_MAX_POS 250 // Ender Configs
+//#define I_MIN_POS 0
+//#define I_MAX_POS 50
+//#define J_MIN_POS 0
+//#define J_MAX_POS 50
+//#define K_MIN_POS 0
+//#define K_MAX_POS 50
+//#define U_MIN_POS 0
+//#define U_MAX_POS 50
+//#define V_MIN_POS 0
+//#define V_MAX_POS 50
+//#define W_MIN_POS 0
+//#define W_MAX_POS 50
+
+/**
+ * Software Endstops
+ *
+ * - Prevent moves outside the set machine bounds.
+ * - Individual axes can be disabled, if desired.
+ * - X and Y only apply to Cartesian robots.
+ * - Use 'M211' to set software endstops on/off or report current state
+ */
+
+// Min software endstops constrain movement within minimum coordinate bounds
+#define MIN_SOFTWARE_ENDSTOPS
+#if ENABLED(MIN_SOFTWARE_ENDSTOPS)
+ #define MIN_SOFTWARE_ENDSTOP_X
+ #define MIN_SOFTWARE_ENDSTOP_Y
+ #define MIN_SOFTWARE_ENDSTOP_Z
+ #define MIN_SOFTWARE_ENDSTOP_I
+ #define MIN_SOFTWARE_ENDSTOP_J
+ #define MIN_SOFTWARE_ENDSTOP_K
+ #define MIN_SOFTWARE_ENDSTOP_U
+ #define MIN_SOFTWARE_ENDSTOP_V
+ #define MIN_SOFTWARE_ENDSTOP_W
+#endif
+
+// Max software endstops constrain movement within maximum coordinate bounds
+#define MAX_SOFTWARE_ENDSTOPS
+#if ENABLED(MAX_SOFTWARE_ENDSTOPS)
+ #define MAX_SOFTWARE_ENDSTOP_X
+ #define MAX_SOFTWARE_ENDSTOP_Y
+ #define MAX_SOFTWARE_ENDSTOP_Z
+ #define MAX_SOFTWARE_ENDSTOP_I
+ #define MAX_SOFTWARE_ENDSTOP_J
+ #define MAX_SOFTWARE_ENDSTOP_K
+ #define MAX_SOFTWARE_ENDSTOP_U
+ #define MAX_SOFTWARE_ENDSTOP_V
+ #define MAX_SOFTWARE_ENDSTOP_W
+#endif
+
+#if EITHER(MIN_SOFTWARE_ENDSTOPS, MAX_SOFTWARE_ENDSTOPS)
+ //#define SOFT_ENDSTOPS_MENU_ITEM // Enable/Disable software endstops from the LCD
+#endif
+
+/**
+ * Filament Runout Sensors
+ * Mechanical or opto endstops are used to check for the presence of filament.
+ *
+ * IMPORTANT: Runout will only trigger if Marlin is aware that a print job is running.
+ * Marlin knows a print job is running when:
+ * 1. Running a print job from media started with M24.
+ * 2. The Print Job Timer has been started with M75.
+ * 3. The heaters were turned on and PRINTJOB_TIMER_AUTOSTART is enabled.
+ *
+ * RAMPS-based boards use SERVO3_PIN for the first runout sensor.
+ * For other boards you may need to define FIL_RUNOUT_PIN, FIL_RUNOUT2_PIN, etc.
+ */
+#define FILAMENT_RUNOUT_SENSOR // MRiscoC Enabled runout sensor support
+#if ENABLED(FILAMENT_RUNOUT_SENSOR)
+ #define FIL_RUNOUT_ENABLED_DEFAULT true // Enable the sensor on startup. Override with M412 followed by M500.
+ #define NUM_RUNOUT_SENSORS 1 // Number of sensors, up to one per extruder. Define a FIL_RUNOUT#_PIN for each.
+
+ #define FIL_RUNOUT_STATE LOW // Pin state indicating that filament is NOT present.
+ #define FIL_RUNOUT_PULLUP // Use internal pullup for filament runout pins.
+ //#define FIL_RUNOUT_PULLDOWN // Use internal pulldown for filament runout pins.
+ //#define WATCH_ALL_RUNOUT_SENSORS // Execute runout script on any triggering sensor, not only for the active extruder.
+ // This is automatically enabled for MIXING_EXTRUDERs.
+
+ // Override individually if the runout sensors vary
+ //#define FIL_RUNOUT1_STATE LOW
+ //#define FIL_RUNOUT1_PULLUP
+ //#define FIL_RUNOUT1_PULLDOWN
+
+ //#define FIL_RUNOUT2_STATE LOW
+ //#define FIL_RUNOUT2_PULLUP
+ //#define FIL_RUNOUT2_PULLDOWN
+
+ //#define FIL_RUNOUT3_STATE LOW
+ //#define FIL_RUNOUT3_PULLUP
+ //#define FIL_RUNOUT3_PULLDOWN
+
+ //#define FIL_RUNOUT4_STATE LOW
+ //#define FIL_RUNOUT4_PULLUP
+ //#define FIL_RUNOUT4_PULLDOWN
+
+ //#define FIL_RUNOUT5_STATE LOW
+ //#define FIL_RUNOUT5_PULLUP
+ //#define FIL_RUNOUT5_PULLDOWN
+
+ //#define FIL_RUNOUT6_STATE LOW
+ //#define FIL_RUNOUT6_PULLUP
+ //#define FIL_RUNOUT6_PULLDOWN
+
+ //#define FIL_RUNOUT7_STATE LOW
+ //#define FIL_RUNOUT7_PULLUP
+ //#define FIL_RUNOUT7_PULLDOWN
+
+ //#define FIL_RUNOUT8_STATE LOW
+ //#define FIL_RUNOUT8_PULLUP
+ //#define FIL_RUNOUT8_PULLDOWN
+
+ // Commands to execute on filament runout.
+ // With multiple runout sensors use the %c placeholder for the current tool in commands (e.g., "M600 T%c")
+ // NOTE: After 'M412 H1' the host handles filament runout and this script does not apply.
+ #define FILAMENT_RUNOUT_SCRIPT "M600"
+
+ // After a runout is detected, continue printing this length of filament
+ // before executing the runout script. Useful for a sensor at the end of
+ // a feed tube. Requires 4 bytes SRAM per sensor, plus 4 bytes overhead.
+ #define FILAMENT_RUNOUT_DISTANCE_MM 25 // MRiscoC Customizable by menu
+
+ #ifdef FILAMENT_RUNOUT_DISTANCE_MM
+ // Enable this option to use an encoder disc that toggles the runout pin
+ // as the filament moves. (Be sure to set FILAMENT_RUNOUT_DISTANCE_MM
+ // large enough to avoid false positives.)
+ //#define FILAMENT_MOTION_SENSOR
+ #endif
+#endif
+
+//===========================================================================
+//=============================== Bed Leveling ==============================
+//===========================================================================
+// @section calibrate
+
+/**
+ * Choose one of the options below to enable G29 Bed Leveling. The parameters
+ * and behavior of G29 will change depending on your selection.
+ *
+ * If using a Probe for Z Homing, enable Z_SAFE_HOMING also!
+ *
+ * - AUTO_BED_LEVELING_3POINT
+ * Probe 3 arbitrary points on the bed (that aren't collinear)
+ * You specify the XY coordinates of all 3 points.
+ * The result is a single tilted plane. Best for a flat bed.
+ *
+ * - AUTO_BED_LEVELING_LINEAR
+ * Probe several points in a grid.
+ * You specify the rectangle and the density of sample points.
+ * The result is a single tilted plane. Best for a flat bed.
+ *
+ * - AUTO_BED_LEVELING_BILINEAR
+ * Probe several points in a grid.
+ * You specify the rectangle and the density of sample points.
+ * The result is a mesh, best for large or uneven beds.
+ *
+ * - AUTO_BED_LEVELING_UBL (Unified Bed Leveling)
+ * A comprehensive bed leveling system combining the features and benefits
+ * of other systems. UBL also includes integrated Mesh Generation, Mesh
+ * Validation and Mesh Editing systems.
+ *
+ * - MESH_BED_LEVELING
+ * Probe a grid manually
+ * The result is a mesh, suitable for large or uneven beds. (See BILINEAR.)
+ * For machines without a probe, Mesh Bed Leveling provides a method to perform
+ * leveling in steps so you can manually adjust the Z height at each grid-point.
+ * With an LCD controller the process is guided step-by-step.
+ */
+//#define AUTO_BED_LEVELING_3POINT
+//#define AUTO_BED_LEVELING_LINEAR
+//#define AUTO_BED_LEVELING_BILINEAR
+//#define AUTO_BED_LEVELING_UBL
+#define MESH_BED_LEVELING // Manual Mesh
+
+/**
+ * Normally G28 leaves leveling disabled on completion. Enable one of
+ * these options to restore the prior leveling state or to always enable
+ * leveling immediately after G28.
+ */
+//#define RESTORE_LEVELING_AFTER_G28
+//#define ENABLE_LEVELING_AFTER_G28
+
+/**
+ * Auto-leveling needs preheating
+ */
+#define PREHEAT_BEFORE_LEVELING // MRiscoC Heatting to compensate thermal expansions
+#if ENABLED(PREHEAT_BEFORE_LEVELING)
+ #define LEVELING_NOZZLE_TEMP 175 // (°C) Only applies to E0 at this time // Preheat nozzle without oozing
+ #define LEVELING_BED_TEMP 50
+#endif
+
+/**
+ * Bed Distance Sensor
+ *
+ * Measures the distance from bed to nozzle with accuracy of 0.01mm.
+ * For information about this sensor https://github.com/markniu/Bed_Distance_sensor
+ * Uses I2C port, so it requires I2C library markyue/Panda_SoftMasterI2C.
+ */
+//#define BD_SENSOR
+
+/**
+ * Enable detailed logging of G28, G29, M48, etc.
+ * Turn on with the command 'M111 S32'.
+ * NOTE: Requires a lot of PROGMEM!
+ */
+//#define DEBUG_LEVELING_FEATURE
+
+#if ANY(MESH_BED_LEVELING, AUTO_BED_LEVELING_UBL, PROBE_MANUALLY)
+ // Set a height for the start of manual adjustment
+ #define MANUAL_PROBE_START_Z 0.2 // (mm) Comment out to use the last-measured height
+#endif
+
+#if ANY(MESH_BED_LEVELING, AUTO_BED_LEVELING_BILINEAR, AUTO_BED_LEVELING_UBL)
+ /**
+ * Gradually reduce leveling correction until a set height is reached,
+ * at which point movement will be level to the machine's XY plane.
+ * The height can be set with M420 Z
+ */
+ #define ENABLE_LEVELING_FADE_HEIGHT
+ #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
+ #define DEFAULT_LEVELING_FADE_HEIGHT 10.0 // (mm) Default fade height.
+ #endif
+
+ /**
+ * For Cartesian machines, instead of dividing moves on mesh boundaries,
+ * split up moves into short segments like a Delta. This follows the
+ * contours of the bed more closely than edge-to-edge straight moves.
+ */
+ #define SEGMENT_LEVELED_MOVES
+ #define LEVELED_SEGMENT_LENGTH 5.0 // (mm) Length of all segments (except the last one)
+
+ /**
+ * Enable the G26 Mesh Validation Pattern tool.
+ */
+ //#define G26_MESH_VALIDATION
+ #if ENABLED(G26_MESH_VALIDATION)
+ #define MESH_TEST_NOZZLE_SIZE 0.4 // (mm) Diameter of primary nozzle.
+ #define MESH_TEST_LAYER_HEIGHT 0.2 // (mm) Default layer height for G26.
+ #define MESH_TEST_HOTEND_TEMP 205 // (°C) Default nozzle temperature for G26.
+ #define MESH_TEST_BED_TEMP 60 // (°C) Default bed temperature for G26.
+ #define G26_XY_FEEDRATE 20 // (mm/s) Feedrate for G26 XY moves.
+ #define G26_XY_FEEDRATE_TRAVEL 100 // (mm/s) Feedrate for G26 XY travel moves.
+ #define G26_RETRACT_MULTIPLIER 1.0 // G26 Q (retraction) used by default between mesh test elements.
+ #endif
+
+#endif
+
+#if EITHER(AUTO_BED_LEVELING_LINEAR, AUTO_BED_LEVELING_BILINEAR)
+
+ // Set the number of grid points per dimension.
+ #define GRID_MAX_POINTS_X 5 // MRiscoC Customizable by menu
+ #define GRID_MAX_POINTS_Y GRID_MAX_POINTS_X
+
+ // Probe along the Y axis, advancing X after each column
+ //#define PROBE_Y_FIRST
+
+ #if ENABLED(AUTO_BED_LEVELING_BILINEAR)
+
+ // Beyond the probed grid, continue the implied tilt?
+ // Default is to maintain the height of the nearest edge.
+ //#define EXTRAPOLATE_BEYOND_GRID // MRiscoC Disabled for better accuracy at edges
+
+ //
+ // Subdivision of the grid by Catmull-Rom method.
+ // Synthesizes intermediate points to produce a more detailed mesh.
+ //
+ //#define ABL_BILINEAR_SUBDIVISION
+ #if ENABLED(ABL_BILINEAR_SUBDIVISION)
+ // Number of subdivisions between probe points
+ #define BILINEAR_SUBDIVISIONS 3
+ #endif
+
+ #endif
+
+#elif ENABLED(AUTO_BED_LEVELING_UBL)
+
+ //===========================================================================
+ //========================= Unified Bed Leveling ============================
+ //===========================================================================
+
+ //#define MESH_EDIT_GFX_OVERLAY // Display a graphics overlay while editing the mesh
+
+ #define MESH_INSET 25 // Set Mesh bounds as an inset region of the bed // MRiscoC Center mesh
+ #define GRID_MAX_POINTS_X 5 // Don't use more than 15 points per axis, implementation limited. // MRiscoC Customizable by menu
+ #define GRID_MAX_POINTS_Y GRID_MAX_POINTS_X
+
+ //#define UBL_HILBERT_CURVE // Use Hilbert distribution for less travel when probing multiple points
+
+ #define UBL_MESH_EDIT_MOVES_Z // Sophisticated users prefer no movement of nozzle
+ #define UBL_SAVE_ACTIVE_ON_M500 // Save the currently active mesh in the current slot on M500
+
+ //#define UBL_Z_RAISE_WHEN_OFF_MESH 2.5 // When the nozzle is off the mesh, this value is used
+ // as the Z-Height correction value.
+
+ //#define UBL_MESH_WIZARD // Run several commands in a row to get a complete mesh
+
+ /**
+ * Probing not allowed within the position of an obstacle.
+ */
+ //#define AVOID_OBSTACLES
+ #if ENABLED(AVOID_OBSTACLES)
+ #define CLIP_W 23 // Bed clip width, should be padded a few mm over its physical size
+ #define CLIP_H 14 // Bed clip height, should be padded a few mm over its physical size
+
+ // Obstacle Rectangles defined as { X1, Y1, X2, Y2 }
+ #define OBSTACLE1 { (X_BED_SIZE) / 4 - (CLIP_W) / 2, 0, (X_BED_SIZE) / 4 + (CLIP_W) / 2, CLIP_H }
+ #define OBSTACLE2 { (X_BED_SIZE) * 3 / 4 - (CLIP_W) / 2, 0, (X_BED_SIZE) * 3 / 4 + (CLIP_W) / 2, CLIP_H }
+ #define OBSTACLE3 { (X_BED_SIZE) / 4 - (CLIP_W) / 2, (Y_BED_SIZE) - (CLIP_H), (X_BED_SIZE) / 4 + (CLIP_W) / 2, Y_BED_SIZE }
+ #define OBSTACLE4 { (X_BED_SIZE) * 3 / 4 - (CLIP_W) / 2, (Y_BED_SIZE) - (CLIP_H), (X_BED_SIZE) * 3 / 4 + (CLIP_W) / 2, Y_BED_SIZE }
+
+ // The probed grid must be inset for G29 J. This is okay, since it is
+ // only used to compute a linear transformation for the mesh itself.
+ #define G29J_MESH_TILT_MARGIN ((CLIP_H) + 1)
+ #endif
+
+#elif ENABLED(MESH_BED_LEVELING)
+
+ //===========================================================================
+ //=================================== Mesh ==================================
+ //===========================================================================
+
+ #define MESH_INSET 25 // Set Mesh bounds as an inset region of the bed // MRiscoC Center mesh
+ #define GRID_MAX_POINTS_X 5 // Don't use more than 7 points per axis, implementation limited. // MRiscoC Customizable by menu
+ #define GRID_MAX_POINTS_Y GRID_MAX_POINTS_X
+
+ //#define MESH_G28_REST_ORIGIN // After homing all axes ('G28' or 'G28 XYZ') rest Z at Z_MIN_POS
+
+#endif // BED_LEVELING
+
+/**
+ * Add a bed leveling sub-menu for ABL or MBL.
+ * Include a guided procedure if manual probing is enabled.
+ */
+//#define LCD_BED_LEVELING
+
+#if ENABLED(LCD_BED_LEVELING)
+ #define MESH_EDIT_Z_STEP 0.025 // (mm) Step size while manually probing Z axis.
+ #define LCD_PROBE_Z_RANGE 4 // (mm) Z Range centered on Z_MIN_POS for LCD Z adjustment
+ //#define MESH_EDIT_MENU // Add a menu to edit mesh points
+#endif
+
+// Add a menu item to move between bed corners for manual bed adjustment
+#define LCD_BED_TRAMMING // ProUI has a bed tramming menu
+
+#if ENABLED(LCD_BED_TRAMMING)
+ #define BED_TRAMMING_INSET_LFRB { 30, 30, 30, 30 } // (mm) Left, Front, Right, Back insets
+ #define BED_TRAMMING_HEIGHT 0.0 // (mm) Z height of nozzle at leveling points
+ #define BED_TRAMMING_Z_HOP 5.0 // (mm) Z height of nozzle between leveling points
+ //#define BED_TRAMMING_INCLUDE_CENTER // Move to the center after the last corner
+ //#define BED_TRAMMING_USE_PROBE
+ #if ENABLED(BED_TRAMMING_USE_PROBE)
+ #define BED_TRAMMING_PROBE_TOLERANCE 0.1 // (mm)
+ #define BED_TRAMMING_VERIFY_RAISED // After adjustment triggers the probe, re-probe to verify
+ //#define BED_TRAMMING_AUDIO_FEEDBACK
+ #endif
+
+ /**
+ * Corner Leveling Order
+ *
+ * Set 2 or 4 points. When 2 points are given, the 3rd is the center of the opposite edge.
+ *
+ * LF Left-Front RF Right-Front
+ * LB Left-Back RB Right-Back
+ *
+ * Examples:
+ *
+ * Default {LF,RB,LB,RF} {LF,RF} {LB,LF}
+ * LB --------- RB LB --------- RB LB --------- RB LB --------- RB
+ * | 4 3 | | 3 2 | | <3> | | 1 |
+ * | | | | | | | <3>|
+ * | 1 2 | | 1 4 | | 1 2 | | 2 |
+ * LF --------- RF LF --------- RF LF --------- RF LF --------- RF
+ */
+ #define BED_TRAMMING_LEVELING_ORDER { LF, RF, RB, LB }
+#endif
+
+/**
+ * Commands to execute at the end of G29 probing.
+ * Useful to retract or move the Z probe out of the way.
+ */
+//#define Z_PROBE_END_SCRIPT "G1 Z10 F12000\nG1 X15 Y330\nG1 Z0.5\nG1 Z10"
+
+// @section homing
+
+// The center of the bed is at (X=0, Y=0)
+//#define BED_CENTER_AT_0_0
+
+// Manually set the home position. Leave these undefined for automatic settings.
+// For DELTA this is the top-center of the Cartesian print volume.
+//#define MANUAL_X_HOME_POS 0
+//#define MANUAL_Y_HOME_POS 0
+//#define MANUAL_Z_HOME_POS 0
+//#define MANUAL_I_HOME_POS 0
+//#define MANUAL_J_HOME_POS 0
+//#define MANUAL_K_HOME_POS 0
+//#define MANUAL_U_HOME_POS 0
+//#define MANUAL_V_HOME_POS 0
+//#define MANUAL_W_HOME_POS 0
+
+/**
+ * Use "Z Safe Homing" to avoid homing with a Z probe outside the bed area.
+ *
+ * - Moves the Z probe (or nozzle) to a defined XY point before Z homing.
+ * - Allows Z homing only when XY positions are known and trusted.
+ * - If stepper drivers sleep, XY homing may be required again before Z homing.
+ */
+//#define Z_SAFE_HOMING
+
+#if ENABLED(Z_SAFE_HOMING)
+ #define Z_SAFE_HOMING_X_POINT X_CENTER // X point for Z homing
+ #define Z_SAFE_HOMING_Y_POINT Y_CENTER // Y point for Z homing
+#endif
+
+// Homing speeds (linear=mm/min, rotational=°/min)
+#define HOMING_FEEDRATE_MM_M { (50*60), (50*60), (10*60) } // MRiscoC Homing speed-up
+
+// Validate that endstops are triggered on homing moves
+#define VALIDATE_HOMING_ENDSTOPS
+
+// @section calibrate
+
+/**
+ * Bed Skew Compensation
+ *
+ * This feature corrects for misalignment in the XYZ axes.
+ *
+ * Take the following steps to get the bed skew in the XY plane:
+ * 1. Print a test square (e.g., https://www.thingiverse.com/thing:2563185)
+ * 2. For XY_DIAG_AC measure the diagonal A to C
+ * 3. For XY_DIAG_BD measure the diagonal B to D
+ * 4. For XY_SIDE_AD measure the edge A to D
+ *
+ * Marlin automatically computes skew factors from these measurements.
+ * Skew factors may also be computed and set manually:
+ *
+ * - Compute AB : SQRT(2*AC*AC+2*BD*BD-4*AD*AD)/2
+ * - XY_SKEW_FACTOR : TAN(PI/2-ACOS((AC*AC-AB*AB-AD*AD)/(2*AB*AD)))
+ *
+ * If desired, follow the same procedure for XZ and YZ.
+ * Use these diagrams for reference:
+ *
+ * Y Z Z
+ * ^ B-------C ^ B-------C ^ B-------C
+ * | / / | / / | / /
+ * | / / | / / | / /
+ * | A-------D | A-------D | A-------D
+ * +-------------->X +-------------->X +-------------->Y
+ * XY_SKEW_FACTOR XZ_SKEW_FACTOR YZ_SKEW_FACTOR
+ */
+//#define SKEW_CORRECTION
+
+#if ENABLED(SKEW_CORRECTION)
+ // Input all length measurements here:
+ #define XY_DIAG_AC 282.8427124746
+ #define XY_DIAG_BD 282.8427124746
+ #define XY_SIDE_AD 200
+
+ // Or, set the XY skew factor directly:
+ //#define XY_SKEW_FACTOR 0.0
+
+ //#define SKEW_CORRECTION_FOR_Z
+ #if ENABLED(SKEW_CORRECTION_FOR_Z)
+ #define XZ_DIAG_AC 282.8427124746
+ #define XZ_DIAG_BD 282.8427124746
+ #define YZ_DIAG_AC 282.8427124746
+ #define YZ_DIAG_BD 282.8427124746
+ #define YZ_SIDE_AD 200
+
+ // Or, set the Z skew factors directly:
+ //#define XZ_SKEW_FACTOR 0.0
+ //#define YZ_SKEW_FACTOR 0.0
+ #endif
+
+ // Enable this option for M852 to set skew at runtime
+ //#define SKEW_CORRECTION_GCODE
+#endif
+
+//=============================================================================
+//============================= Additional Features ===========================
+//=============================================================================
+
+// @section eeprom
+
+/**
+ * EEPROM
+ *
+ * Persistent storage to preserve configurable settings across reboots.
+ *
+ * M500 - Store settings to EEPROM.
+ * M501 - Read settings from EEPROM. (i.e., Throw away unsaved changes)
+ * M502 - Revert settings to "factory" defaults. (Follow with M500 to init the EEPROM.)
+ */
+#define EEPROM_SETTINGS // Persistent storage with M500 and M501 // Ender Configs
+//#define DISABLE_M503 // Saves ~2700 bytes of flash. Disable for release!
+#define EEPROM_CHITCHAT // Give feedback on EEPROM commands. Disable to save PROGMEM.
+#define EEPROM_BOOT_SILENT // Keep M503 quiet and only give errors during first load
+#if ENABLED(EEPROM_SETTINGS)
+ #define EEPROM_AUTO_INIT // Init EEPROM automatically on any errors. // Ender Configs
+ #define EEPROM_INIT_NOW // Init EEPROM on first boot after a new build. // MRiscoC Reset EEPROM on first boot
+#endif
+
+// @section host
+
+//
+// Host Keepalive
+//
+// When enabled Marlin will send a busy status message to the host
+// every couple of seconds when it can't accept commands.
+//
+#define HOST_KEEPALIVE_FEATURE // Disable this if your host doesn't like keepalive messages
+#define DEFAULT_KEEPALIVE_INTERVAL 2 // Number of seconds between "busy" messages. Set with M113.
+#define BUSY_WHILE_HEATING // Some hosts require "busy" messages even during heating
+
+// @section units
+
+//
+// G20/G21 Inch mode support
+//
+//#define INCH_MODE_SUPPORT
+
+//
+// M149 Set temperature units support
+//
+//#define TEMPERATURE_UNITS_SUPPORT
+
+// @section temperature
+
+//
+// Preheat Constants - Up to 10 are supported without changes
+//
+#define PREHEAT_1_LABEL "PLA"
+#define PREHEAT_1_TEMP_HOTEND 195
+#define PREHEAT_1_TEMP_BED 60
+#define PREHEAT_1_TEMP_CHAMBER 35
+#define PREHEAT_1_FAN_SPEED 128 // Value from 0 to 255
+
+#define PREHEAT_2_LABEL "ABS"
+#define PREHEAT_2_TEMP_HOTEND 240
+#define PREHEAT_2_TEMP_BED 90
+#define PREHEAT_2_TEMP_CHAMBER 35
+#define PREHEAT_2_FAN_SPEED 128 // Value from 0 to 255
+
+#define PREHEAT_3_LABEL "PETG"
+#define PREHEAT_3_TEMP_HOTEND 230
+#define PREHEAT_3_TEMP_BED 80
+#define PREHEAT_3_FAN_SPEED 128
+
+#define PREHEAT_4_LABEL "CUSTOM"
+#define PREHEAT_4_TEMP_HOTEND 190
+#define PREHEAT_4_TEMP_BED 50
+#define PREHEAT_4_FAN_SPEED 128
+
+// @section motion
+
+/**
+ * Nozzle Park
+ *
+ * Park the nozzle at the given XYZ position on idle or G27.
+ *
+ * The "P" parameter controls the action applied to the Z axis:
+ *
+ * P0 (Default) If Z is below park Z raise the nozzle.
+ * P1 Raise the nozzle always to Z-park height.
+ * P2 Raise the nozzle by Z-park amount, limited to Z_MAX_POS.
+ */
+#define NOZZLE_PARK_FEATURE // MRiscoC Enabled
+
+#if ENABLED(NOZZLE_PARK_FEATURE)
+ // Specify a park position as { X, Y, Z_raise }
+ #define NOZZLE_PARK_POINT { (X_BED_SIZE + 10), (Y_MAX_POS - 10), 20 }
+ #define NOZZLE_PARK_MOVE 0 // Park motion: 0 = XY Move, 1 = X Only, 2 = Y Only, 3 = X before Y, 4 = Y before X
+ #define NOZZLE_PARK_Z_RAISE_MIN 0 // (mm) Always raise Z by at least this distance // MRiscoC uses Park Z Raise from 0 to avoid backlash issues
+ #define NOZZLE_PARK_XY_FEEDRATE 100 // (mm/s) X and Y axes feedrate (also used for delta Z axis)
+ #define NOZZLE_PARK_Z_FEEDRATE 5 // (mm/s) Z axis feedrate (not used for delta printers)
+#endif
+
+/**
+ * Clean Nozzle Feature
+ *
+ * Adds the G12 command to perform a nozzle cleaning process.
+ *
+ * Parameters:
+ * P Pattern
+ * S Strokes / Repetitions
+ * T Triangles (P1 only)
+ *
+ * Patterns:
+ * P0 Straight line (default). This process requires a sponge type material
+ * at a fixed bed location. "S" specifies strokes (i.e. back-forth motions)
+ * between the start / end points.
+ *
+ * P1 Zig-zag pattern between (X0, Y0) and (X1, Y1), "T" specifies the
+ * number of zig-zag triangles to do. "S" defines the number of strokes.
+ * Zig-zags are done in whichever is the narrower dimension.
+ * For example, "G12 P1 S1 T3" will execute:
+ *
+ * --
+ * | (X0, Y1) | /\ /\ /\ | (X1, Y1)
+ * | | / \ / \ / \ |
+ * A | | / \ / \ / \ |
+ * | | / \ / \ / \ |
+ * | (X0, Y0) | / \/ \/ \ | (X1, Y0)
+ * -- +--------------------------------+
+ * |________|_________|_________|
+ * T1 T2 T3
+ *
+ * P2 Circular pattern with middle at NOZZLE_CLEAN_CIRCLE_MIDDLE.
+ * "R" specifies the radius. "S" specifies the stroke count.
+ * Before starting, the nozzle moves to NOZZLE_CLEAN_START_POINT.
+ *
+ * Caveats: The ending Z should be the same as starting Z.
+ */
+//#define NOZZLE_CLEAN_FEATURE
+
+#if ENABLED(NOZZLE_CLEAN_FEATURE)
+ // Default number of pattern repetitions
+ #define NOZZLE_CLEAN_STROKES 12
+
+ // Default number of triangles
+ #define NOZZLE_CLEAN_TRIANGLES 3
+
+ // Specify positions for each tool as { { X, Y, Z }, { X, Y, Z } }
+ // Dual hotend system may use { { -20, (Y_BED_SIZE / 2), (Z_MIN_POS + 1) }, { 420, (Y_BED_SIZE / 2), (Z_MIN_POS + 1) }}
+ #define NOZZLE_CLEAN_START_POINT { { 30, 30, (Z_MIN_POS + 1) } }
+ #define NOZZLE_CLEAN_END_POINT { { 100, 60, (Z_MIN_POS + 1) } }
+
+ // Circular pattern radius
+ #define NOZZLE_CLEAN_CIRCLE_RADIUS 6.5
+ // Circular pattern circle fragments number
+ #define NOZZLE_CLEAN_CIRCLE_FN 10
+ // Middle point of circle
+ #define NOZZLE_CLEAN_CIRCLE_MIDDLE NOZZLE_CLEAN_START_POINT
+
+ // Move the nozzle to the initial position after cleaning
+ #define NOZZLE_CLEAN_GOBACK
+
+ // For a purge/clean station that's always at the gantry height (thus no Z move)
+ //#define NOZZLE_CLEAN_NO_Z
+
+ // For a purge/clean station mounted on the X axis
+ //#define NOZZLE_CLEAN_NO_Y
+
+ // Require a minimum hotend temperature for cleaning
+ #define NOZZLE_CLEAN_MIN_TEMP 170
+ //#define NOZZLE_CLEAN_HEATUP // Heat up the nozzle instead of skipping wipe
+
+ // Explicit wipe G-code script applies to a G12 with no arguments.
+ //#define WIPE_SEQUENCE_COMMANDS "G1 X-17 Y25 Z10 F4000\nG1 Z1\nM114\nG1 X-17 Y25\nG1 X-17 Y95\nG1 X-17 Y25\nG1 X-17 Y95\nG1 X-17 Y25\nG1 X-17 Y95\nG1 X-17 Y25\nG1 X-17 Y95\nG1 X-17 Y25\nG1 X-17 Y95\nG1 X-17 Y25\nG1 X-17 Y95\nG1 Z15\nM400\nG0 X-10.0 Y-9.0"
+
+#endif
+
+// @section host
+
+/**
+ * Print Job Timer
+ *
+ * Automatically start and stop the print job timer on M104/M109/M140/M190/M141/M191.
+ * The print job timer will only be stopped if the bed/chamber target temp is
+ * below BED_MINTEMP/CHAMBER_MINTEMP.
+ *
+ * M104 (hotend, no wait) - high temp = none, low temp = stop timer
+ * M109 (hotend, wait) - high temp = start timer, low temp = stop timer
+ * M140 (bed, no wait) - high temp = none, low temp = stop timer
+ * M190 (bed, wait) - high temp = start timer, low temp = none
+ * M141 (chamber, no wait) - high temp = none, low temp = stop timer
+ * M191 (chamber, wait) - high temp = start timer, low temp = none
+ *
+ * For M104/M109, high temp is anything over EXTRUDE_MINTEMP / 2.
+ * For M140/M190, high temp is anything over BED_MINTEMP.
+ * For M141/M191, high temp is anything over CHAMBER_MINTEMP.
+ *
+ * The timer can also be controlled with the following commands:
+ *
+ * M75 - Start the print job timer
+ * M76 - Pause the print job timer
+ * M77 - Stop the print job timer
+ */
+#define PRINTJOB_TIMER_AUTOSTART
+
+// @section stats
+
+/**
+ * Print Counter
+ *
+ * Track statistical data such as:
+ *
+ * - Total print jobs
+ * - Total successful print jobs
+ * - Total failed print jobs
+ * - Total time printing
+ *
+ * View the current statistics with M78.
+ */
+#define PRINTCOUNTER // MRiscoC Enable Print Statistics
+#if ENABLED(PRINTCOUNTER)
+ #define PRINTCOUNTER_SAVE_INTERVAL 60 // (minutes) EEPROM save interval during print. A value of 0 will save stats at end of print.
+#endif
+
+// @section security
+
+/**
+ * Password
+ *
+ * Set a numerical password for the printer which can be requested:
+ *
+ * - When the printer boots up
+ * - Upon opening the 'Print from Media' Menu
+ * - When SD printing is completed or aborted
+ *
+ * The following G-codes can be used:
+ *
+ * M510 - Lock Printer. Blocks all commands except M511.
+ * M511 - Unlock Printer.
+ * M512 - Set, Change and Remove Password.
+ *
+ * If you forget the password and get locked out you'll need to re-flash
+ * the firmware with the feature disabled, reset EEPROM, and (optionally)
+ * re-flash the firmware again with this feature enabled.
+ */
+//#define PASSWORD_FEATURE
+#if ENABLED(PASSWORD_FEATURE)
+ #define PASSWORD_LENGTH 4 // (#) Number of digits (1-9). 3 or 4 is recommended
+ #define PASSWORD_ON_STARTUP
+ #define PASSWORD_UNLOCK_GCODE // Unlock with the M511 P command. Disable to prevent brute-force attack.
+ #define PASSWORD_CHANGE_GCODE // Change the password with M512 P S.
+ //#define PASSWORD_ON_SD_PRINT_MENU // This does not prevent gcodes from running
+ //#define PASSWORD_AFTER_SD_PRINT_END
+ //#define PASSWORD_AFTER_SD_PRINT_ABORT
+ //#include "Configuration_Secure.h" // External file with PASSWORD_DEFAULT_VALUE
+#endif
+
+//=============================================================================
+//============================= LCD and SD support ============================
+//=============================================================================
+
+// @section interface
+
+/**
+ * LCD LANGUAGE
+ *
+ * Select the language to display on the LCD. These languages are available:
+ *
+ * en, an, bg, ca, cz, da, de, el, el_CY, es, eu, fi, fr, gl, hr, hu, it,
+ * jp_kana, ko_KR, nl, pl, pt, pt_br, ro, ru, sk, sv, tr, uk, vi, zh_CN, zh_TW
+ *
+ * :{ 'en':'English', 'an':'Aragonese', 'bg':'Bulgarian', 'ca':'Catalan', 'cz':'Czech', 'da':'Danish', 'de':'German', 'el':'Greek (Greece)', 'el_CY':'Greek (Cyprus)', 'es':'Spanish', 'eu':'Basque-Euskera', 'fi':'Finnish', 'fr':'French', 'gl':'Galician', 'hr':'Croatian', 'hu':'Hungarian', 'it':'Italian', 'jp_kana':'Japanese', 'ko_KR':'Korean (South Korea)', 'nl':'Dutch', 'pl':'Polish', 'pt':'Portuguese', 'pt_br':'Portuguese (Brazilian)', 'ro':'Romanian', 'ru':'Russian', 'sk':'Slovak', 'sv':'Swedish', 'tr':'Turkish', 'uk':'Ukrainian', 'vi':'Vietnamese', 'zh_CN':'Chinese (Simplified)', 'zh_TW':'Chinese (Traditional)' }
+ */
+#define LCD_LANGUAGE en
+
+/**
+ * LCD Character Set
+ *
+ * Note: This option is NOT applicable to Graphical Displays.
+ *
+ * All character-based LCDs provide ASCII plus one of these
+ * language extensions:
+ *
+ * - JAPANESE ... the most common
+ * - WESTERN ... with more accented characters
+ * - CYRILLIC ... for the Russian language
+ *
+ * To determine the language extension installed on your controller:
+ *
+ * - Compile and upload with LCD_LANGUAGE set to 'test'
+ * - Click the controller to view the LCD menu
+ * - The LCD will display Japanese, Western, or Cyrillic text
+ *
+ * See https://marlinfw.org/docs/development/lcd_language.html
+ *
+ * :['JAPANESE', 'WESTERN', 'CYRILLIC']
+ */
+#define DISPLAY_CHARSET_HD44780 JAPANESE
+
+/**
+ * Info Screen Style (0:Classic, 1:Průša)
+ *
+ * :[0:'Classic', 1:'Průša']
+ */
+#define LCD_INFO_SCREEN_STYLE 0
+
+/**
+ * SD CARD
+ *
+ * SD Card support is disabled by default. If your controller has an SD slot,
+ * you must uncomment the following option or it won't work.
+ */
+#define SDSUPPORT // Ender Configs
+
+/**
+ * SD CARD: ENABLE CRC
+ *
+ * Use CRC checks and retries on the SD communication.
+ */
+//#define SD_CHECK_AND_RETRY
+
+/**
+ * LCD Menu Items
+ *
+ * Disable all menus and only display the Status Screen, or
+ * just remove some extraneous menu items to recover space.
+ */
+//#define NO_LCD_MENUS
+//#define SLIM_LCD_MENUS
+
+//
+// ENCODER SETTINGS
+//
+// This option overrides the default number of encoder pulses needed to
+// produce one step. Should be increased for high-resolution encoders.
+//
+#define ENCODER_PULSES_PER_STEP 4 // Ender Configs
+
+//
+// Use this option to override the number of step signals required to
+// move between next/prev menu items.
+//
+#define ENCODER_STEPS_PER_MENU_ITEM 1 // Ender Configs
+
+/**
+ * Encoder Direction Options
+ *
+ * Test your encoder's behavior first with both options disabled.
+ *
+ * Reversed Value Edit and Menu Nav? Enable REVERSE_ENCODER_DIRECTION.
+ * Reversed Menu Navigation only? Enable REVERSE_MENU_DIRECTION.
+ * Reversed Value Editing only? Enable BOTH options.
+ */
+
+//
+// This option reverses the encoder direction everywhere.
+//
+// Set this option if CLOCKWISE causes values to DECREASE
+//
+//#define REVERSE_ENCODER_DIRECTION
+
+//
+// This option reverses the encoder direction for navigating LCD menus.
+//
+// If CLOCKWISE normally moves DOWN this makes it go UP.
+// If CLOCKWISE normally moves UP this makes it go DOWN.
+//
+//#define REVERSE_MENU_DIRECTION
+
+//
+// This option reverses the encoder direction for Select Screen.
+//
+// If CLOCKWISE normally moves LEFT this makes it go RIGHT.
+// If CLOCKWISE normally moves RIGHT this makes it go LEFT.
+//
+//#define REVERSE_SELECT_DIRECTION
+
+//
+// Encoder EMI Noise Filter
+//
+// This option increases encoder samples to filter out phantom encoder clicks caused by EMI noise.
+//
+//#define ENCODER_NOISE_FILTER
+#if ENABLED(ENCODER_NOISE_FILTER)
+ #define ENCODER_SAMPLES 10
+#endif
+
+//
+// Individual Axis Homing
+//
+// Add individual axis homing items (Home X, Home Y, and Home Z) to the LCD menu.
+//
+//#define INDIVIDUAL_AXIS_HOMING_MENU
+#define INDIVIDUAL_AXIS_HOMING_SUBMENU
+
+//
+// SPEAKER/BUZZER
+//
+// If you have a speaker that can produce tones, enable it here.
+// By default Marlin assumes you have a buzzer with a fixed frequency.
+//
+//#define SPEAKER
+
+//
+// The duration and frequency for the UI feedback sound.
+// Set these to 0 to disable audio feedback in the LCD menus.
+//
+// Note: Test audio output with the G-Code:
+// M300 S P
+//
+#define LCD_FEEDBACK_FREQUENCY_DURATION_MS 2 // Ender Configs
+#define LCD_FEEDBACK_FREQUENCY_HZ 5000 // Ender Configs
+
+//=============================================================================
+//======================== LCD / Controller Selection =========================
+//======================== (Character-based LCDs) =========================
+//=============================================================================
+// @section lcd
+
+//
+// RepRapDiscount Smart Controller.
+// https://reprap.org/wiki/RepRapDiscount_Smart_Controller
+//
+// Note: Usually sold with a white PCB.
+//
+//#define REPRAP_DISCOUNT_SMART_CONTROLLER
+
+//
+// GT2560 (YHCB2004) LCD Display
+//
+// Requires Testato, Koepel softwarewire library and
+// Andriy Golovnya's LiquidCrystal_AIP31068 library.
+//
+//#define YHCB2004
+
+//
+// Original RADDS LCD Display+Encoder+SDCardReader
+// http://doku.radds.org/dokumentation/lcd-display/
+//
+//#define RADDS_DISPLAY
+
+//
+// ULTIMAKER Controller.
+//
+//#define ULTIMAKERCONTROLLER
+
+//
+// ULTIPANEL as seen on Thingiverse.
+//
+//#define ULTIPANEL
+
+//
+// PanelOne from T3P3 (via RAMPS 1.4 AUX2/AUX3)
+// https://reprap.org/wiki/PanelOne
+//
+//#define PANEL_ONE
+
+//
+// GADGETS3D G3D LCD/SD Controller
+// https://reprap.org/wiki/RAMPS_1.3/1.4_GADGETS3D_Shield_with_Panel
+//
+// Note: Usually sold with a blue PCB.
+//
+//#define G3D_PANEL
+
+//
+// RigidBot Panel V1.0
+// http://www.inventapart.com/
+//
+//#define RIGIDBOT_PANEL
+
+//
+// Makeboard 3D Printer Parts 3D Printer Mini Display 1602 Mini Controller
+// https://www.aliexpress.com/item/32765887917.html
+//
+//#define MAKEBOARD_MINI_2_LINE_DISPLAY_1602
+
+//
+// ANET and Tronxy 20x4 Controller
+//
+//#define ZONESTAR_LCD // Requires ADC_KEYPAD_PIN to be assigned to an analog pin.
+ // This LCD is known to be susceptible to electrical interference
+ // which scrambles the display. Pressing any button clears it up.
+ // This is a LCD2004 display with 5 analog buttons.
+
+//
+// Generic 16x2, 16x4, 20x2, or 20x4 character-based LCD.
+//
+//#define ULTRA_LCD
+
+//=============================================================================
+//======================== LCD / Controller Selection =========================
+//===================== (I2C and Shift-Register LCDs) =====================
+//=============================================================================
+
+//
+// CONTROLLER TYPE: I2C
+//
+// Note: These controllers require the installation of Arduino's LiquidCrystal_I2C
+// library. For more info: https://github.com/kiyoshigawa/LiquidCrystal_I2C
+//
+
+//
+// Elefu RA Board Control Panel
+// https://web.archive.org/web/20140823033947/http://www.elefu.com/index.php?route=product/product&product_id=53
+//
+//#define RA_CONTROL_PANEL
+
+//
+// Sainsmart (YwRobot) LCD Displays
+//
+// These require F.Malpartida's LiquidCrystal_I2C library
+// https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home
+//
+//#define LCD_SAINSMART_I2C_1602
+//#define LCD_SAINSMART_I2C_2004
+
+//
+// Generic LCM1602 LCD adapter
+//
+//#define LCM1602
+
+//
+// PANELOLU2 LCD with status LEDs,
+// separate encoder and click inputs.
+//
+// Note: This controller requires Arduino's LiquidTWI2 library v1.2.3 or later.
+// For more info: https://github.com/lincomatic/LiquidTWI2
+//
+// Note: The PANELOLU2 encoder click input can either be directly connected to
+// a pin (if BTN_ENC defined to != -1) or read through I2C (when BTN_ENC == -1).
+//
+//#define LCD_I2C_PANELOLU2
+
+//
+// Panucatt VIKI LCD with status LEDs,
+// integrated click & L/R/U/D buttons, separate encoder inputs.
+//
+//#define LCD_I2C_VIKI
+
+//
+// CONTROLLER TYPE: Shift register panels
+//
+
+//
+// 2-wire Non-latching LCD SR from https://goo.gl/aJJ4sH
+// LCD configuration: https://reprap.org/wiki/SAV_3D_LCD
+//
+//#define SAV_3DLCD
+
+//
+// 3-wire SR LCD with strobe using 74HC4094
+// https://github.com/mikeshub/SailfishLCD
+// Uses the code directly from Sailfish
+//
+//#define FF_INTERFACEBOARD
+
+//
+// TFT GLCD Panel with Marlin UI
+// Panel connected to main board by SPI or I2C interface.
+// See https://github.com/Serhiy-K/TFTGLCDAdapter
+//
+//#define TFTGLCD_PANEL_SPI
+//#define TFTGLCD_PANEL_I2C
+
+//=============================================================================
+//======================= LCD / Controller Selection =======================
+//========================= (Graphical LCDs) ========================
+//=============================================================================
+
+//
+// CONTROLLER TYPE: Graphical 128x64 (DOGM)
+//
+// IMPORTANT: The U8glib library is required for Graphical Display!
+// https://github.com/olikraus/U8glib_Arduino
+//
+// NOTE: If the LCD is unresponsive you may need to reverse the plugs.
+//
+
+//
+// RepRapDiscount FULL GRAPHIC Smart Controller
+// https://reprap.org/wiki/RepRapDiscount_Full_Graphic_Smart_Controller
+//
+//#define REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER
+
+//
+// K.3D Full Graphic Smart Controller
+//
+//#define K3D_FULL_GRAPHIC_SMART_CONTROLLER
+
+//
+// ReprapWorld Graphical LCD
+// https://reprapworld.com/electronics/3d-printer-modules/autonomous-printing/graphical-lcd-screen-v1-0/
+//
+//#define REPRAPWORLD_GRAPHICAL_LCD
+
+//
+// Activate one of these if you have a Panucatt Devices
+// Viki 2.0 or mini Viki with Graphic LCD
+// https://www.panucatt.com
+//
+//#define VIKI2
+//#define miniVIKI
+
+//
+// Alfawise Ex8 printer LCD marked as WYH L12864 COG
+//
+//#define WYH_L12864
+
+//
+// MakerLab Mini Panel with graphic
+// controller and SD support - https://reprap.org/wiki/Mini_panel
+//
+//#define MINIPANEL
+
+//
+// MaKr3d Makr-Panel with graphic controller and SD support.
+// https://reprap.org/wiki/MaKr3d_MaKrPanel
+//
+//#define MAKRPANEL
+
+//
+// Adafruit ST7565 Full Graphic Controller.
+// https://github.com/eboston/Adafruit-ST7565-Full-Graphic-Controller/
+//
+//#define ELB_FULL_GRAPHIC_CONTROLLER
+
+//
+// BQ LCD Smart Controller shipped by
+// default with the BQ Hephestos 2 and Witbox 2.
+//
+//#define BQ_LCD_SMART_CONTROLLER
+
+//
+// Cartesio UI
+// http://mauk.cc/webshop/cartesio-shop/electronics/user-interface
+//
+//#define CARTESIO_UI
+
+//
+// LCD for Melzi Card with Graphical LCD
+//
+//#define LCD_FOR_MELZI
+
+//
+// Original Ulticontroller from Ultimaker 2 printer with SSD1309 I2C display and encoder
+// https://github.com/Ultimaker/Ultimaker2/tree/master/1249_Ulticontroller_Board_(x1)
+//
+//#define ULTI_CONTROLLER
+
+//
+// MKS MINI12864 with graphic controller and SD support
+// https://reprap.org/wiki/MKS_MINI_12864
+//
+//#define MKS_MINI_12864
+
+//
+// MKS MINI12864 V3 is an alias for FYSETC_MINI_12864_2_1. Type A/B. NeoPixel RGB Backlight.
+//
+//#define MKS_MINI_12864_V3
+
+//
+// MKS LCD12864A/B with graphic controller and SD support. Follows MKS_MINI_12864 pinout.
+// https://www.aliexpress.com/item/33018110072.html
+//
+//#define MKS_LCD12864A
+//#define MKS_LCD12864B
+
+//
+// FYSETC variant of the MINI12864 graphic controller with SD support
+// https://wiki.fysetc.com/Mini12864_Panel/
+//
+//#define FYSETC_MINI_12864_X_X // Type C/D/E/F. No tunable RGB Backlight by default
+//#define FYSETC_MINI_12864_1_2 // Type C/D/E/F. Simple RGB Backlight (always on)
+//#define FYSETC_MINI_12864_2_0 // Type A/B. Discreet RGB Backlight
+//#define FYSETC_MINI_12864_2_1 // Type A/B. NeoPixel RGB Backlight
+//#define FYSETC_GENERIC_12864_1_1 // Larger display with basic ON/OFF backlight.
+
+//
+// BigTreeTech Mini 12864 V1.0 is an alias for FYSETC_MINI_12864_2_1. Type A/B. NeoPixel RGB Backlight.
+//
+//#define BTT_MINI_12864_V1
+
+//
+// Factory display for Creality CR-10
+// https://www.aliexpress.com/item/32833148327.html
+//
+// This is RAMPS-compatible using a single 10-pin connector.
+// (For CR-10 owners who want to replace the Melzi Creality board but retain the display)
+//
+//#define CR10_STOCKDISPLAY
+
+//
+// Ender-2 OEM display, a variant of the MKS_MINI_12864
+//
+//#define ENDER2_STOCKDISPLAY
+
+//
+// ANET and Tronxy Graphical Controller
+//
+// Anet 128x64 full graphics lcd with rotary encoder as used on Anet A6
+// A clone of the RepRapDiscount full graphics display but with
+// different pins/wiring (see pins_ANET_10.h). Enable one of these.
+//
+//#define ANET_FULL_GRAPHICS_LCD
+//#define ANET_FULL_GRAPHICS_LCD_ALT_WIRING
+
+//
+// AZSMZ 12864 LCD with SD
+// https://www.aliexpress.com/item/32837222770.html
+//
+//#define AZSMZ_12864
+
+//
+// Silvergate GLCD controller
+// https://github.com/android444/Silvergate
+//
+//#define SILVER_GATE_GLCD_CONTROLLER
+
+//
+// eMotion Tech LCD with SD
+// https://www.reprap-france.com/produit/1234568748-ecran-graphique-128-x-64-points-2-1
+//
+//#define EMOTION_TECH_LCD
+
+//=============================================================================
+//============================== OLED Displays ==============================
+//=============================================================================
+
+//
+// SSD1306 OLED full graphics generic display
+//
+//#define U8GLIB_SSD1306
+
+//
+// SAV OLEd LCD module support using either SSD1306 or SH1106 based LCD modules
+//
+//#define SAV_3DGLCD
+#if ENABLED(SAV_3DGLCD)
+ #define U8GLIB_SSD1306
+ //#define U8GLIB_SH1106
+#endif
+
+//
+// TinyBoy2 128x64 OLED / Encoder Panel
+//
+//#define OLED_PANEL_TINYBOY2
+
+//
+// MKS OLED 1.3" 128×64 Full Graphics Controller
+// https://reprap.org/wiki/MKS_12864OLED
+//
+// Tiny, but very sharp OLED display
+//
+//#define MKS_12864OLED // Uses the SH1106 controller (default)
+//#define MKS_12864OLED_SSD1306 // Uses the SSD1306 controller
+
+//
+// Zonestar OLED 128×64 Full Graphics Controller
+//
+//#define ZONESTAR_12864LCD // Graphical (DOGM) with ST7920 controller
+//#define ZONESTAR_12864OLED // 1.3" OLED with SH1106 controller (default)
+//#define ZONESTAR_12864OLED_SSD1306 // 0.96" OLED with SSD1306 controller
+
+//
+// Einstart S OLED SSD1306
+//
+//#define U8GLIB_SH1106_EINSTART
+
+//
+// Overlord OLED display/controller with i2c buzzer and LEDs
+//
+//#define OVERLORD_OLED
+
+//
+// FYSETC OLED 2.42" 128×64 Full Graphics Controller with WS2812 RGB
+// Where to find : https://www.aliexpress.com/item/4000345255731.html
+//#define FYSETC_242_OLED_12864 // Uses the SSD1309 controller
+
+//
+// K.3D SSD1309 OLED 2.42" 128×64 Full Graphics Controller
+//
+//#define K3D_242_OLED_CONTROLLER // Software SPI
+
+//=============================================================================
+//========================== Extensible UI Displays ===========================
+//=============================================================================
+
+/**
+ * DGUS Touch Display with DWIN OS. (Choose one.)
+ *
+ * ORIGIN (Marlin DWIN_SET)
+ * - Download https://github.com/coldtobi/Marlin_DGUS_Resources
+ * - Copy the downloaded DWIN_SET folder to the SD card.
+ * - Product: https://www.aliexpress.com/item/32993409517.html
+ *
+ * FYSETC (Supplier default)
+ * - Download https://github.com/FYSETC/FYSTLCD-2.0
+ * - Copy the downloaded SCREEN folder to the SD card.
+ * - Product: https://www.aliexpress.com/item/32961471929.html
+ *
+ * HIPRECY (Supplier default)
+ * - Download https://github.com/HiPrecy/Touch-Lcd-LEO
+ * - Copy the downloaded DWIN_SET folder to the SD card.
+ *
+ * MKS (MKS-H43) (Supplier default)
+ * - Download https://github.com/makerbase-mks/MKS-H43
+ * - Copy the downloaded DWIN_SET folder to the SD card.
+ * - Product: https://www.aliexpress.com/item/1005002008179262.html
+ *
+ * RELOADED (T5UID1)
+ * - Download https://github.com/Desuuuu/DGUS-reloaded/releases
+ * - Copy the downloaded DWIN_SET folder to the SD card.
+ *
+ * IA_CREALITY (T5UID1)
+ * - Download https://github.com/InsanityAutomation/Marlin/raw/CrealityDwin_2.0/TM3D_Combined480272_Landscape_V7.7z
+ * - Copy the downloaded DWIN_SET folder to the SD card.
+ *
+ * Flash display with DGUS Displays for Marlin:
+ * - Format the SD card to FAT32 with an allocation size of 4kb.
+ * - Download files as specified for your type of display.
+ * - Plug the microSD card into the back of the display.
+ * - Boot the display and wait for the update to complete.
+ */
+//#define DGUS_LCD_UI ORIGIN
+#if DGUS_UI_IS(MKS)
+ #define USE_MKS_GREEN_UI
+#elif DGUS_UI_IS(IA_CREALITY)
+ //#define LCD_SCREEN_ROTATE 90 // Portrait Mode or 800x480 displays
+#endif
+
+//
+// Touch-screen LCD for Malyan M200/M300 printers
+//
+//#define MALYAN_LCD
+
+//
+// Touch UI for FTDI EVE (FT800/FT810) displays
+// See Configuration_adv.h for all configuration options.
+//
+//#define TOUCH_UI_FTDI_EVE
+
+//
+// Touch-screen LCD for Anycubic printers
+//
+//#define ANYCUBIC_LCD_I3MEGA
+//#define ANYCUBIC_LCD_CHIRON
+#if EITHER(ANYCUBIC_LCD_I3MEGA, ANYCUBIC_LCD_CHIRON)
+ //#define ANYCUBIC_LCD_DEBUG
+ //#define ANYCUBIC_LCD_GCODE_EXT // Add ".gcode" to menu entries for DGUS clone compatibility
+#endif
+
+//
+// 320x240 Nextion 2.8" serial TFT Resistive Touch Screen NX3224T028
+//
+//#define NEXTION_TFT
+
+//
+// Third-party or vendor-customized controller interfaces.
+// Sources should be installed in 'src/lcd/extui'.
+//
+//#define EXTENSIBLE_UI
+
+#if ENABLED(EXTENSIBLE_UI)
+ //#define EXTUI_LOCAL_BEEPER // Enables use of local Beeper pin with external display
+#endif
+
+//=============================================================================
+//=============================== Graphical TFTs ==============================
+//=============================================================================
+
+/**
+ * Specific TFT Model Presets. Enable one of the following options
+ * or enable TFT_GENERIC and set sub-options.
+ */
+
+//
+// 480x320, 3.5", SPI Display with Rotary Encoder from MKS
+// Usually paired with MKS Robin Nano V2 & V3
+//
+//#define MKS_TS35_V2_0
+
+//
+// 320x240, 2.4", FSMC Display From MKS
+// Usually paired with MKS Robin Nano V1.2
+//
+//#define MKS_ROBIN_TFT24
+
+//
+// 320x240, 2.8", FSMC Display From MKS
+// Usually paired with MKS Robin Nano V1.2
+//
+//#define MKS_ROBIN_TFT28
+
+//
+// 320x240, 3.2", FSMC Display From MKS
+// Usually paired with MKS Robin Nano V1.2
+//
+//#define MKS_ROBIN_TFT32
+
+//
+// 480x320, 3.5", FSMC Display From MKS
+// Usually paired with MKS Robin Nano V1.2
+//
+//#define MKS_ROBIN_TFT35
+
+//
+// 480x272, 4.3", FSMC Display From MKS
+//
+//#define MKS_ROBIN_TFT43
+
+//
+// 320x240, 3.2", FSMC Display From MKS
+// Usually paired with MKS Robin
+//
+//#define MKS_ROBIN_TFT_V1_1R
+
+//
+// 480x320, 3.5", FSMC Stock Display from Tronxy
+//
+//#define TFT_TRONXY_X5SA
+
+//
+// 480x320, 3.5", FSMC Stock Display from AnyCubic
+//
+//#define ANYCUBIC_TFT35
+
+//
+// 320x240, 2.8", FSMC Stock Display from Longer/Alfawise
+//
+//#define LONGER_LK_TFT28
+
+//
+// 320x240, 2.8", FSMC Stock Display from ET4
+//
+//#define ANET_ET4_TFT28
+
+//
+// 480x320, 3.5", FSMC Stock Display from ET5
+//
+//#define ANET_ET5_TFT35
+
+//
+// 1024x600, 7", RGB Stock Display with Rotary Encoder from BIQU-BX
+//
+//#define BIQU_BX_TFT70
+
+//
+// 480x320, 3.5", SPI Stock Display with Rotary Encoder from BIQU B1 SE Series
+//
+//#define BTT_TFT35_SPI_V1_0
+
+//
+// Generic TFT with detailed options
+//
+//#define TFT_GENERIC
+#if ENABLED(TFT_GENERIC)
+ // :[ 'AUTO', 'ST7735', 'ST7789', 'ST7796', 'R61505', 'ILI9328', 'ILI9341', 'ILI9488' ]
+ #define TFT_DRIVER AUTO
+
+ // Interface. Enable one of the following options:
+ //#define TFT_INTERFACE_FSMC
+ //#define TFT_INTERFACE_SPI
+
+ // TFT Resolution. Enable one of the following options:
+ //#define TFT_RES_320x240
+ //#define TFT_RES_480x272
+ //#define TFT_RES_480x320
+ //#define TFT_RES_1024x600
+#endif
+
+/**
+ * TFT UI - User Interface Selection. Enable one of the following options:
+ *
+ * TFT_CLASSIC_UI - Emulated DOGM - 128x64 Upscaled
+ * TFT_COLOR_UI - Marlin Default Menus, Touch Friendly, using full TFT capabilities
+ * TFT_LVGL_UI - A Modern UI using LVGL
+ *
+ * For LVGL_UI also copy the 'assets' folder from the build directory to the
+ * root of your SD card, together with the compiled firmware.
+ */
+//#define TFT_CLASSIC_UI
+//#define TFT_COLOR_UI
+//#define TFT_LVGL_UI
+
+#if ENABLED(TFT_COLOR_UI)
+ /**
+ * TFT Font for Color_UI. Choose one of the following:
+ *
+ * NOTOSANS - Default font with antialiasing. Supports Latin Extended and non-Latin characters.
+ * UNIFONT - Lightweight font, no antialiasing. Supports Latin Extended and non-Latin characters.
+ * HELVETICA - Lightweight font, no antialiasing. Supports Basic Latin (0x0020-0x007F) and Latin-1 Supplement (0x0080-0x00FF) characters only.
+ */
+ #define TFT_FONT NOTOSANS
+
+ //#define TFT_SHARED_SPI // SPI is shared between TFT display and other devices. Disable async data transfer
+#endif
+
+#if ENABLED(TFT_LVGL_UI)
+ //#define MKS_WIFI_MODULE // MKS WiFi module
+#endif
+
+/**
+ * TFT Rotation. Set to one of the following values:
+ *
+ * TFT_ROTATE_90, TFT_ROTATE_90_MIRROR_X, TFT_ROTATE_90_MIRROR_Y,
+ * TFT_ROTATE_180, TFT_ROTATE_180_MIRROR_X, TFT_ROTATE_180_MIRROR_Y,
+ * TFT_ROTATE_270, TFT_ROTATE_270_MIRROR_X, TFT_ROTATE_270_MIRROR_Y,
+ * TFT_MIRROR_X, TFT_MIRROR_Y, TFT_NO_ROTATION
+ */
+//#define TFT_ROTATION TFT_NO_ROTATION
+
+//=============================================================================
+//============================ Other Controllers ============================
+//=============================================================================
+
+//
+// Ender-3 v2 OEM display. A DWIN display with Rotary Encoder.
+//
+//#define DWIN_CREALITY_LCD // Creality UI
+#define DWIN_LCD_PROUI // Pro UI by MRiscoC
+#define USE_STOCK_DWIN_SET
+
+// Professional firmware features:
+#define ProUIex 1
+#if ProUIex
+ #define HAS_GCODE_PREVIEW 1
+ #define HAS_TOOLBAR 1
+#endif
+#define HAS_PLOT 1
+#define HAS_ESDIAG 1
+#define HAS_CGCODE 1
+#define HAS_LOCKSCREEN 1
+//#define HAS_SD_EXTENDER 1 // Enable to support SD card extender cables
+#define MESH_EDIT_MENU
+#define SHOW_REAL_POS
+//#define ACTIVATE_MESH_ITEM // Allows temporary enabling of mesh leveling
+#define RUNOUT_TUNE_ITEM
+#define PLR_TUNE_ITEM
+//#define JD_TUNE_ITEM // Enable only if Juntion Deviation is enabled
+//#define ADVK_TUNE_ITEM // Enable only if Linear Advance is enabled
+//#define MEDIASORT_MENU_ITEM // Allows enable/disable file list sorting
+
+//#define DWIN_CREALITY_LCD_JYERSUI // Jyers UI by Jacob Myers
+//#define DWIN_MARLINUI_PORTRAIT // MarlinUI (portrait orientation)
+//#define DWIN_MARLINUI_LANDSCAPE // MarlinUI (landscape orientation)
+
+//
+// Touch Screen Settings
+//
+//#define TOUCH_SCREEN
+#if ENABLED(TOUCH_SCREEN)
+ #define BUTTON_DELAY_EDIT 50 // (ms) Button repeat delay for edit screens
+ #define BUTTON_DELAY_MENU 250 // (ms) Button repeat delay for menus
+
+ //#define DISABLE_ENCODER // Disable the click encoder, if any
+ //#define TOUCH_IDLE_SLEEP_MINS 5 // (minutes) Display Sleep after a period of inactivity. Set with M255 S.
+
+ #define TOUCH_SCREEN_CALIBRATION
+
+ //#define TOUCH_CALIBRATION_X 12316
+ //#define TOUCH_CALIBRATION_Y -8981
+ //#define TOUCH_OFFSET_X -43
+ //#define TOUCH_OFFSET_Y 257
+ //#define TOUCH_ORIENTATION TOUCH_LANDSCAPE
+
+ #if BOTH(TOUCH_SCREEN_CALIBRATION, EEPROM_SETTINGS)
+ #define TOUCH_CALIBRATION_AUTO_SAVE // Auto save successful calibration values to EEPROM
+ #endif
+
+ #if ENABLED(TFT_COLOR_UI)
+ //#define SINGLE_TOUCH_NAVIGATION
+ #endif
+#endif
+
+//
+// RepRapWorld REPRAPWORLD_KEYPAD v1.1
+// https://reprapworld.com/products/electronics/ramps/keypad_v1_0_fully_assembled/
+//
+//#define REPRAPWORLD_KEYPAD
+//#define REPRAPWORLD_KEYPAD_MOVE_STEP 10.0 // (mm) Distance to move per key-press
+
+//
+// EasyThreeD ET-4000+ with button input and status LED
+//
+//#define EASYTHREED_UI
+
+//=============================================================================
+//=============================== Extra Features ==============================
+//=============================================================================
+
+// @section fans
+
+// Set number of user-controlled fans. Disable to use all board-defined fans.
+// :[1,2,3,4,5,6,7,8]
+//#define NUM_M106_FANS 1
+
+// Use software PWM to drive the fan, as for the heaters. This uses a very low frequency
+// which is not as annoying as with the hardware PWM. On the other hand, if this frequency
+// is too low, you should also increment SOFT_PWM_SCALE.
+#define FAN_SOFT_PWM // Ender Configs
+
+// Incrementing this by 1 will double the software PWM frequency,
+// affecting heaters, and the fan if FAN_SOFT_PWM is enabled.
+// However, control resolution will be halved for each increment;
+// at zero value, there are 128 effective control positions.
+// :[0,1,2,3,4,5,6,7]
+#define SOFT_PWM_SCALE 0
+
+// If SOFT_PWM_SCALE is set to a value higher than 0, dithering can
+// be used to mitigate the associated resolution loss. If enabled,
+// some of the PWM cycles are stretched so on average the desired
+// duty cycle is attained.
+//#define SOFT_PWM_DITHER
+
+// @section extras
+
+// Support for the BariCUDA Paste Extruder
+//#define BARICUDA
+
+// @section lights
+
+// Temperature status LEDs that display the hotend and bed temperature.
+// If all hotends, bed temperature, and target temperature are under 54C
+// then the BLUE led is on. Otherwise the RED led is on. (1C hysteresis)
+//#define TEMP_STAT_LEDS
+
+// Support for BlinkM/CyzRgb
+//#define BLINKM
+
+// Support for PCA9632 PWM LED driver
+//#define PCA9632
+
+// Support for PCA9533 PWM LED driver
+//#define PCA9533
+
+/**
+ * RGB LED / LED Strip Control
+ *
+ * Enable support for an RGB LED connected to 5V digital pins, or
+ * an RGB Strip connected to MOSFETs controlled by digital pins.
+ *
+ * Adds the M150 command to set the LED (or LED strip) color.
+ * If pins are PWM capable (e.g., 4, 5, 6, 11) then a range of
+ * luminance values can be set from 0 to 255.
+ * For NeoPixel LED an overall brightness parameter is also available.
+ *
+ * === CAUTION ===
+ * LED Strips require a MOSFET Chip between PWM lines and LEDs,
+ * as the Arduino cannot handle the current the LEDs will require.
+ * Failure to follow this precaution can destroy your Arduino!
+ *
+ * NOTE: A separate 5V power supply is required! The NeoPixel LED needs
+ * more current than the Arduino 5V linear regulator can produce.
+ *
+ * Requires PWM frequency between 50 <> 100Hz (Check HAL or variant)
+ * Use FAST_PWM_FAN, if possible, to reduce fan noise.
+ */
+
+// LED Type. Enable only one of the following two options:
+//#define RGB_LED
+//#define RGBW_LED
+
+#if EITHER(RGB_LED, RGBW_LED)
+ //#define RGB_LED_R_PIN 34
+ //#define RGB_LED_G_PIN 43
+ //#define RGB_LED_B_PIN 35
+ //#define RGB_LED_W_PIN -1
+#endif
+
+#if ANY(RGB_LED, RGBW_LED, PCA9632)
+ //#define RGB_STARTUP_TEST // For PWM pins, fade between all colors
+ #if ENABLED(RGB_STARTUP_TEST)
+ #define RGB_STARTUP_TEST_INNER_MS 10 // (ms) Reduce or increase fading speed
+ #endif
+#endif
+
+// Support for Adafruit NeoPixel LED driver
+//#define NEOPIXEL_LED
+#if ENABLED(NEOPIXEL_LED)
+ #define NEOPIXEL_TYPE NEO_GRBW // NEO_GRBW, NEO_RGBW, NEO_GRB, NEO_RBG, etc.
+ // See https://github.com/adafruit/Adafruit_NeoPixel/blob/master/Adafruit_NeoPixel.h
+ #define NEOPIXEL_PIN PA13 // LED driving pin
+ //#define NEOPIXEL2_TYPE NEOPIXEL_TYPE
+ //#define NEOPIXEL2_PIN 5
+ #define NEOPIXEL_PIXELS 30 // Number of LEDs in the strip. (Longest strip when NEOPIXEL2_SEPARATE is disabled.)
+ #define NEOPIXEL_IS_SEQUENTIAL // Sequential display for temperature change - LED by LED. Disable to change all LEDs at once.
+ #define NEOPIXEL_BRIGHTNESS 127 // Initial brightness (0-255)
+ //#define NEOPIXEL_STARTUP_TEST // Cycle through colors at startup
+
+ // Support for second Adafruit NeoPixel LED driver controlled with M150 S1 ...
+ //#define NEOPIXEL2_SEPARATE
+ #if ENABLED(NEOPIXEL2_SEPARATE)
+ #define NEOPIXEL2_PIXELS 15 // Number of LEDs in the second strip
+ #define NEOPIXEL2_BRIGHTNESS 127 // Initial brightness (0-255)
+ #define NEOPIXEL2_STARTUP_TEST // Cycle through colors at startup
+ #define NEOPIXEL_M150_DEFAULT -1 // Default strip for M150 without 'S'. Use -1 to set all by default.
+ #else
+ //#define NEOPIXEL2_INSERIES // Default behavior is NeoPixel 2 in parallel
+ #endif
+
+ // Use some of the NeoPixel LEDs for static (background) lighting
+ //#define NEOPIXEL_BKGD_INDEX_FIRST 0 // Index of the first background LED
+ //#define NEOPIXEL_BKGD_INDEX_LAST 5 // Index of the last background LED
+ //#define NEOPIXEL_BKGD_COLOR { 255, 255, 255, 0 } // R, G, B, W
+ //#define NEOPIXEL_BKGD_TIMEOUT_COLOR { 25, 25, 25, 0 } // R, G, B, W
+ //#define NEOPIXEL_BKGD_ALWAYS_ON // Keep the backlight on when other NeoPixels are off
+#endif
+
+/**
+ * Printer Event LEDs
+ *
+ * During printing, the LEDs will reflect the printer status:
+ *
+ * - Gradually change from blue to violet as the heated bed gets to target temp
+ * - Gradually change from violet to red as the hotend gets to temperature
+ * - Change to white to illuminate work surface
+ * - Change to green once print has finished
+ * - Turn off after the print has finished and the user has pushed a button
+ */
+#if ANY(BLINKM, RGB_LED, RGBW_LED, PCA9632, PCA9533, NEOPIXEL_LED)
+ #define PRINTER_EVENT_LEDS
+#endif
+
+// @section servos
+
+/**
+ * Number of servos
+ *
+ * For some servo-related options NUM_SERVOS will be set automatically.
+ * Set this manually if there are extra servos needing manual control.
+ * Set to 0 to turn off servo support.
+ */
+//#define NUM_SERVOS 3 // Note: Servo index starts with 0 for M280-M282 commands
+
+// (ms) Delay before the next move will start, to give the servo time to reach its target angle.
+// 300ms is a good value but you can try less delay.
+// If the servo can't reach the requested position, increase it.
+#define SERVO_DELAY { 300 }
+
+// Only power servos during movement, otherwise leave off to prevent jitter
+//#define DEACTIVATE_SERVOS_AFTER_MOVE
+
+// Edit servo angles with M281 and save to EEPROM with M500
+//#define EDITABLE_SERVO_ANGLES
+
+// Disable servo with M282 to reduce power consumption, noise, and heat when not in use
+//#define SERVO_DETACH_GCODE
diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h
new file mode 100644
index 0000000000..5ce15a7a35
--- /dev/null
+++ b/Marlin/Configuration_adv.h
@@ -0,0 +1,4269 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+// Created by configs generator for Professional firmware
+// https://github.com/mriscoc/Ender3V2S1
+
+/**
+ * Configuration_adv.h
+ *
+ * Advanced settings.
+ * Only change these if you know exactly what you're doing.
+ * Some of these settings can damage your printer if improperly set!
+ *
+ * Basic settings can be found in Configuration.h
+ */
+#define CONFIGURATION_ADV_H_VERSION 02010300
+
+// @section develop
+
+/**
+ * Configuration Export
+ *
+ * Export the configuration as part of the build. (See signature.py)
+ * Output files are saved with the build (e.g., .pio/build/mega2560).
+ *
+ * See `build_all_examples --ini` as an example of config.ini archiving.
+ *
+ * 1 = marlin_config.json - Dictionary containing the configuration.
+ * This file is also generated for CONFIGURATION_EMBEDDING.
+ * 2 = config.ini - File format for PlatformIO preprocessing.
+ * 3 = schema.json - The entire configuration schema. (13 = pattern groups)
+ * 4 = schema.yml - The entire configuration schema.
+ */
+//#define CONFIG_EXPORT 2 // :[1:'JSON', 2:'config.ini', 3:'schema.json', 4:'schema.yml']
+
+//===========================================================================
+//============================= Thermal Settings ============================
+//===========================================================================
+// @section temperature
+
+/**
+ * Thermocouple sensors are quite sensitive to noise. Any noise induced in
+ * the sensor wires, such as by stepper motor wires run in parallel to them,
+ * may result in the thermocouple sensor reporting spurious errors. This
+ * value is the number of errors which can occur in a row before the error
+ * is reported. This allows us to ignore intermittent error conditions while
+ * still detecting an actual failure, which should result in a continuous
+ * stream of errors from the sensor.
+ *
+ * Set this value to 0 to fail on the first error to occur.
+ */
+#define THERMOCOUPLE_MAX_ERRORS 15
+
+//
+// Custom Thermistor 1000 parameters
+//
+#if TEMP_SENSOR_0 == 1000
+ #define HOTEND0_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define HOTEND0_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define HOTEND0_BETA 3950 // Beta value
+ #define HOTEND0_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_1 == 1000
+ #define HOTEND1_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define HOTEND1_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define HOTEND1_BETA 3950 // Beta value
+ #define HOTEND1_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_2 == 1000
+ #define HOTEND2_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define HOTEND2_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define HOTEND2_BETA 3950 // Beta value
+ #define HOTEND2_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_3 == 1000
+ #define HOTEND3_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define HOTEND3_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define HOTEND3_BETA 3950 // Beta value
+ #define HOTEND3_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_4 == 1000
+ #define HOTEND4_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define HOTEND4_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define HOTEND4_BETA 3950 // Beta value
+ #define HOTEND4_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_5 == 1000
+ #define HOTEND5_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define HOTEND5_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define HOTEND5_BETA 3950 // Beta value
+ #define HOTEND5_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_6 == 1000
+ #define HOTEND6_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define HOTEND6_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define HOTEND6_BETA 3950 // Beta value
+ #define HOTEND6_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_7 == 1000
+ #define HOTEND7_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define HOTEND7_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define HOTEND7_BETA 3950 // Beta value
+ #define HOTEND7_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_BED == 1000
+ #define BED_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define BED_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define BED_BETA 3950 // Beta value
+ #define BED_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_CHAMBER == 1000
+ #define CHAMBER_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define CHAMBER_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define CHAMBER_BETA 3950 // Beta value
+ #define CHAMBER_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_COOLER == 1000
+ #define COOLER_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define COOLER_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define COOLER_BETA 3950 // Beta value
+ #define COOLER_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_PROBE == 1000
+ #define PROBE_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define PROBE_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define PROBE_BETA 3950 // Beta value
+ #define PROBE_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_BOARD == 1000
+ #define BOARD_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define BOARD_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define BOARD_BETA 3950 // Beta value
+ #define BOARD_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_REDUNDANT == 1000
+ #define REDUNDANT_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor
+ #define REDUNDANT_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+ #define REDUNDANT_BETA 3950 // Beta value
+ #define REDUNDANT_SH_C_COEFF 0 // Steinhart-Hart C coefficient
+#endif
+
+/**
+ * Thermocouple Options — for MAX6675 (-2), MAX31855 (-3), and MAX31865 (-5).
+ */
+//#define TEMP_SENSOR_FORCE_HW_SPI // Ignore SCK/MOSI/MISO pins; use CS and the default SPI bus.
+//#define MAX31865_SENSOR_WIRES_0 2 // (2-4) Number of wires for the probe connected to a MAX31865 board.
+//#define MAX31865_SENSOR_WIRES_1 2
+//#define MAX31865_SENSOR_WIRES_2 2
+
+//#define MAX31865_50HZ_FILTER // Use a 50Hz filter instead of the default 60Hz.
+//#define MAX31865_USE_READ_ERROR_DETECTION // Treat value spikes (20°C delta in under 1s) as read errors.
+
+//#define MAX31865_USE_AUTO_MODE // Read faster and more often than 1-shot; bias voltage always on; slight effect on RTD temperature.
+//#define MAX31865_MIN_SAMPLING_TIME_MSEC 100 // (ms) 1-shot: minimum read interval. Reduces bias voltage effects by leaving sensor unpowered for longer intervals.
+//#define MAX31865_IGNORE_INITIAL_FAULTY_READS 10 // Ignore some read faults (keeping the temperature reading) to work around a possible issue (#23439).
+
+//#define MAX31865_WIRE_OHMS_0 0.95f // For 2-wire, set the wire resistances for more accurate readings.
+//#define MAX31865_WIRE_OHMS_1 0.0f
+//#define MAX31865_WIRE_OHMS_2 0.0f
+
+/**
+ * Hephestos 2 24V heated bed upgrade kit.
+ * https://store.bq.com/en/heated-bed-kit-hephestos2
+ */
+//#define HEPHESTOS2_HEATED_BED_KIT
+#if ENABLED(HEPHESTOS2_HEATED_BED_KIT)
+ #undef TEMP_SENSOR_BED
+ #define TEMP_SENSOR_BED 70
+ #define HEATER_BED_INVERTING true
+#endif
+
+//
+// Heated Bed Bang-Bang options
+//
+#if DISABLED(PIDTEMPBED)
+ #define BED_CHECK_INTERVAL 5000 // (ms) Interval between checks in bang-bang control
+ #if ENABLED(BED_LIMIT_SWITCHING)
+ #define BED_HYSTERESIS 2 // (°C) Only set the relevant heater state when ABS(T-target) > BED_HYSTERESIS
+ #endif
+#endif
+
+//
+// Heated Chamber options
+//
+#if DISABLED(PIDTEMPCHAMBER)
+ #define CHAMBER_CHECK_INTERVAL 5000 // (ms) Interval between checks in bang-bang control
+ #if ENABLED(CHAMBER_LIMIT_SWITCHING)
+ #define CHAMBER_HYSTERESIS 2 // (°C) Only set the relevant heater state when ABS(T-target) > CHAMBER_HYSTERESIS
+ #endif
+#endif
+
+#if TEMP_SENSOR_CHAMBER
+ //#define HEATER_CHAMBER_PIN P2_04 // Required heater on/off pin (example: SKR 1.4 Turbo HE1 plug)
+ //#define HEATER_CHAMBER_INVERTING false
+ //#define FAN1_PIN -1 // Remove the fan signal on pin P2_04 (example: SKR 1.4 Turbo HE1 plug)
+
+ //#define CHAMBER_FAN // Enable a fan on the chamber
+ #if ENABLED(CHAMBER_FAN)
+ //#define CHAMBER_FAN_INDEX 2 // Index of a fan to repurpose as the chamber fan. (Default: first unused fan)
+ #define CHAMBER_FAN_MODE 2 // Fan control mode: 0=Static; 1=Linear increase when temp is higher than target; 2=V-shaped curve; 3=similar to 1 but fan is always on.
+ #if CHAMBER_FAN_MODE == 0
+ #define CHAMBER_FAN_BASE 255 // Chamber fan PWM (0-255)
+ #elif CHAMBER_FAN_MODE == 1
+ #define CHAMBER_FAN_BASE 128 // Base chamber fan PWM (0-255); turns on when chamber temperature is above the target
+ #define CHAMBER_FAN_FACTOR 25 // PWM increase per °C above target
+ #elif CHAMBER_FAN_MODE == 2
+ #define CHAMBER_FAN_BASE 128 // Minimum chamber fan PWM (0-255)
+ #define CHAMBER_FAN_FACTOR 25 // PWM increase per °C difference from target
+ #elif CHAMBER_FAN_MODE == 3
+ #define CHAMBER_FAN_BASE 128 // Base chamber fan PWM (0-255)
+ #define CHAMBER_FAN_FACTOR 25 // PWM increase per °C above target
+ #endif
+ #endif
+
+ //#define CHAMBER_VENT // Enable a servo-controlled vent on the chamber
+ #if ENABLED(CHAMBER_VENT)
+ #define CHAMBER_VENT_SERVO_NR 1 // Index of the vent servo
+ #define HIGH_EXCESS_HEAT_LIMIT 5 // How much above target temp to consider there is excess heat in the chamber
+ #define LOW_EXCESS_HEAT_LIMIT 3
+ #define MIN_COOLING_SLOPE_TIME_CHAMBER_VENT 20
+ #define MIN_COOLING_SLOPE_DEG_CHAMBER_VENT 1.5
+ #endif
+#endif
+
+//
+// Laser Cooler options
+//
+#if TEMP_SENSOR_COOLER
+ #define COOLER_MINTEMP 8 // (°C)
+ #define COOLER_MAXTEMP 26 // (°C)
+ #define COOLER_DEFAULT_TEMP 16 // (°C)
+ #define TEMP_COOLER_HYSTERESIS 1 // (°C) Temperature proximity considered "close enough" to the target
+ #define COOLER_PIN 8 // Laser cooler on/off pin used to control power to the cooling element (e.g., TEC, External chiller via relay)
+ #define COOLER_INVERTING false
+ #define TEMP_COOLER_PIN 15 // Laser/Cooler temperature sensor pin. ADC is required.
+ #define COOLER_FAN // Enable a fan on the cooler, Fan# 0,1,2,3 etc.
+ #define COOLER_FAN_INDEX 0 // FAN number 0, 1, 2 etc. e.g.
+ #if ENABLED(COOLER_FAN)
+ #define COOLER_FAN_BASE 100 // Base Cooler fan PWM (0-255); turns on when Cooler temperature is above the target
+ #define COOLER_FAN_FACTOR 25 // PWM increase per °C above target
+ #endif
+#endif
+
+//
+// Motherboard Sensor options
+//
+#if TEMP_SENSOR_BOARD
+ #define THERMAL_PROTECTION_BOARD // Halt the printer if the board sensor leaves the temp range below.
+ #define BOARD_MINTEMP 8 // (°C)
+ #define BOARD_MAXTEMP 70 // (°C)
+ #ifndef TEMP_BOARD_PIN
+ //#define TEMP_BOARD_PIN -1 // Board temp sensor pin, if not set in pins file.
+ #endif
+#endif
+
+/**
+ * Thermal Protection provides additional protection to your printer from damage
+ * and fire. Marlin always includes safe min and max temperature ranges which
+ * protect against a broken or disconnected thermistor wire.
+ *
+ * The issue: If a thermistor falls out, it will report the much lower
+ * temperature of the air in the room, and the the firmware will keep
+ * the heater on.
+ *
+ * The solution: Once the temperature reaches the target, start observing.
+ * If the temperature stays too far below the target (hysteresis) for too
+ * long (period), the firmware will halt the machine as a safety precaution.
+ *
+ * If you get false positives for "Thermal Runaway", increase
+ * THERMAL_PROTECTION_HYSTERESIS and/or THERMAL_PROTECTION_PERIOD
+ */
+#if ENABLED(THERMAL_PROTECTION_HOTENDS)
+ #define THERMAL_PROTECTION_PERIOD 40 // Seconds
+ #define THERMAL_PROTECTION_HYSTERESIS 4 // Degrees Celsius
+
+ //#define ADAPTIVE_FAN_SLOWING // Slow part cooling fan if temperature drops
+ #if ENABLED(ADAPTIVE_FAN_SLOWING) && EITHER(MPCTEMP, PIDTEMP)
+ //#define TEMP_TUNING_MAINTAIN_FAN // Don't slow fan speed during M303 or M306 T
+ #endif
+
+ /**
+ * Whenever an M104, M109, or M303 increases the target temperature, the
+ * firmware will wait for the WATCH_TEMP_PERIOD to expire. If the temperature
+ * hasn't increased by WATCH_TEMP_INCREASE degrees, the machine is halted and
+ * requires a hard reset. This test restarts with any M104/M109/M303, but only
+ * if the current temperature is far enough below the target for a reliable
+ * test.
+ *
+ * If you get false positives for "Heating failed", increase WATCH_TEMP_PERIOD
+ * and/or decrease WATCH_TEMP_INCREASE. WATCH_TEMP_INCREASE should not be set
+ * below 2.
+ */
+ #define WATCH_TEMP_PERIOD 40 // Seconds // Ender Configs
+ #define WATCH_TEMP_INCREASE 2 // Degrees Celsius
+#endif
+
+/**
+ * Thermal Protection parameters for the bed are just as above for hotends.
+ */
+#if ENABLED(THERMAL_PROTECTION_BED)
+ #define THERMAL_PROTECTION_BED_PERIOD 180 // Seconds // Ender Configs
+ #define THERMAL_PROTECTION_BED_HYSTERESIS 2 // Degrees Celsius
+
+ /**
+ * As described above, except for the bed (M140/M190/M303).
+ */
+ #define WATCH_BED_TEMP_PERIOD 180 // Seconds // Ender Configs
+ #define WATCH_BED_TEMP_INCREASE 2 // Degrees Celsius
+#endif
+
+/**
+ * Thermal Protection parameters for the heated chamber.
+ */
+#if ENABLED(THERMAL_PROTECTION_CHAMBER)
+ #define THERMAL_PROTECTION_CHAMBER_PERIOD 20 // Seconds
+ #define THERMAL_PROTECTION_CHAMBER_HYSTERESIS 2 // Degrees Celsius
+
+ /**
+ * Heated chamber watch settings (M141/M191).
+ */
+ #define WATCH_CHAMBER_TEMP_PERIOD 60 // Seconds
+ #define WATCH_CHAMBER_TEMP_INCREASE 2 // Degrees Celsius
+#endif
+
+/**
+ * Thermal Protection parameters for the laser cooler.
+ */
+#if ENABLED(THERMAL_PROTECTION_COOLER)
+ #define THERMAL_PROTECTION_COOLER_PERIOD 10 // Seconds
+ #define THERMAL_PROTECTION_COOLER_HYSTERESIS 3 // Degrees Celsius
+
+ /**
+ * Laser cooling watch settings (M143/M193).
+ */
+ #define WATCH_COOLER_TEMP_PERIOD 60 // Seconds
+ #define WATCH_COOLER_TEMP_INCREASE 3 // Degrees Celsius
+#endif
+
+#if ANY(THERMAL_PROTECTION_HOTENDS, THERMAL_PROTECTION_BED, THERMAL_PROTECTION_CHAMBER, THERMAL_PROTECTION_COOLER)
+ /**
+ * Thermal Protection Variance Monitor - EXPERIMENTAL.
+ * Kill the machine on a stuck temperature sensor. Disable if you get false positives.
+ */
+ //#define THERMAL_PROTECTION_VARIANCE_MONITOR // Detect a sensor malfunction preventing temperature updates // MRiscoC disabled because it gives false positives
+#endif
+
+#if ENABLED(PIDTEMP)
+ // Add an additional term to the heater power, proportional to the extrusion speed.
+ // A well-chosen Kc value should add just enough power to melt the increased material volume.
+ //#define PID_EXTRUSION_SCALING
+ #if ENABLED(PID_EXTRUSION_SCALING)
+ #define DEFAULT_Kc (100) // heating power = Kc * e_speed
+ #define LPQ_MAX_LEN 50
+ #endif
+
+ /**
+ * Add an additional term to the heater power, proportional to the fan speed.
+ * A well-chosen Kf value should add just enough power to compensate for power-loss from the cooling fan.
+ * You can either just add a constant compensation with the DEFAULT_Kf value
+ * or follow the instruction below to get speed-dependent compensation.
+ *
+ * Constant compensation (use only with fanspeeds of 0% and 100%)
+ * ---------------------------------------------------------------------
+ * A good starting point for the Kf-value comes from the calculation:
+ * kf = (power_fan * eff_fan) / power_heater * 255
+ * where eff_fan is between 0.0 and 1.0, based on fan-efficiency and airflow to the nozzle / heater.
+ *
+ * Example:
+ * Heater: 40W, Fan: 0.1A * 24V = 2.4W, eff_fan = 0.8
+ * Kf = (2.4W * 0.8) / 40W * 255 = 12.24
+ *
+ * Fan-speed dependent compensation
+ * --------------------------------
+ * 1. To find a good Kf value, set the hotend temperature, wait for it to settle, and enable the fan (100%).
+ * Make sure PID_FAN_SCALING_LIN_FACTOR is 0 and PID_FAN_SCALING_ALTERNATIVE_DEFINITION is not enabled.
+ * If you see the temperature drop repeat the test, increasing the Kf value slowly, until the temperature
+ * drop goes away. If the temperature overshoots after enabling the fan, the Kf value is too big.
+ * 2. Note the Kf-value for fan-speed at 100%
+ * 3. Determine a good value for PID_FAN_SCALING_MIN_SPEED, which is around the speed, where the fan starts moving.
+ * 4. Repeat step 1. and 2. for this fan speed.
+ * 5. Enable PID_FAN_SCALING_ALTERNATIVE_DEFINITION and enter the two identified Kf-values in
+ * PID_FAN_SCALING_AT_FULL_SPEED and PID_FAN_SCALING_AT_MIN_SPEED. Enter the minimum speed in PID_FAN_SCALING_MIN_SPEED
+ */
+ //#define PID_FAN_SCALING
+ #if ENABLED(PID_FAN_SCALING)
+ //#define PID_FAN_SCALING_ALTERNATIVE_DEFINITION
+ #if ENABLED(PID_FAN_SCALING_ALTERNATIVE_DEFINITION)
+ // The alternative definition is used for an easier configuration.
+ // Just figure out Kf at fullspeed (255) and PID_FAN_SCALING_MIN_SPEED.
+ // DEFAULT_Kf and PID_FAN_SCALING_LIN_FACTOR are calculated accordingly.
+
+ #define PID_FAN_SCALING_AT_FULL_SPEED 13.0 //=PID_FAN_SCALING_LIN_FACTOR*255+DEFAULT_Kf
+ #define PID_FAN_SCALING_AT_MIN_SPEED 6.0 //=PID_FAN_SCALING_LIN_FACTOR*PID_FAN_SCALING_MIN_SPEED+DEFAULT_Kf
+ #define PID_FAN_SCALING_MIN_SPEED 10.0 // Minimum fan speed at which to enable PID_FAN_SCALING
+
+ #define DEFAULT_Kf (255.0*PID_FAN_SCALING_AT_MIN_SPEED-PID_FAN_SCALING_AT_FULL_SPEED*PID_FAN_SCALING_MIN_SPEED)/(255.0-PID_FAN_SCALING_MIN_SPEED)
+ #define PID_FAN_SCALING_LIN_FACTOR (PID_FAN_SCALING_AT_FULL_SPEED-DEFAULT_Kf)/255.0
+
+ #else
+ #define PID_FAN_SCALING_LIN_FACTOR (0) // Power loss due to cooling = Kf * (fan_speed)
+ #define DEFAULT_Kf 10 // A constant value added to the PID-tuner
+ #define PID_FAN_SCALING_MIN_SPEED 10 // Minimum fan speed at which to enable PID_FAN_SCALING
+ #endif
+ #endif
+#endif
+
+/**
+ * Automatic Temperature Mode
+ *
+ * Dynamically adjust the hotend target temperature based on planned E moves.
+ *
+ * (Contrast with PID_EXTRUSION_SCALING, which tracks E movement and adjusts PID
+ * behavior using an additional kC value.)
+ *
+ * Autotemp is calculated by (mintemp + factor * mm_per_sec), capped to maxtemp.
+ *
+ * Enable Autotemp Mode with M104/M109 F S B.
+ * Disable by sending M104/M109 with no F parameter (or F0 with AUTOTEMP_PROPORTIONAL).
+ */
+#define AUTOTEMP
+#if ENABLED(AUTOTEMP)
+ #define AUTOTEMP_OLDWEIGHT 0.98 // Factor used to weight previous readings (0.0 < value < 1.0)
+ #define AUTOTEMP_MIN 210
+ #define AUTOTEMP_MAX 250
+ #define AUTOTEMP_FACTOR 0.1f
+ // Turn on AUTOTEMP on M104/M109 by default using proportions set here
+ //#define AUTOTEMP_PROPORTIONAL
+ #if ENABLED(AUTOTEMP_PROPORTIONAL)
+ #define AUTOTEMP_MIN_P 0 // (°C) Added to the target temperature
+ #define AUTOTEMP_MAX_P 5 // (°C) Added to the target temperature
+ #define AUTOTEMP_FACTOR_P 1 // Apply this F parameter by default (overridden by M104/M109 F)
+ #endif
+#endif
+
+// Show Temperature ADC value
+// Enable for M105 to include ADC values read from temperature sensors.
+//#define SHOW_TEMP_ADC_VALUES
+
+/**
+ * High Temperature Thermistor Support
+ *
+ * Thermistors able to support high temperature tend to have a hard time getting
+ * good readings at room and lower temperatures. This means TEMP_SENSOR_X_RAW_LO_TEMP
+ * will probably be caught when the heating element first turns on during the
+ * preheating process, which will trigger a MINTEMP error as a safety measure
+ * and force stop everything.
+ * To circumvent this limitation, we allow for a preheat time (during which,
+ * MINTEMP error won't be triggered) and add a min_temp buffer to handle
+ * aberrant readings.
+ *
+ * If you want to enable this feature for your hotend thermistor(s)
+ * uncomment and set values > 0 in the constants below
+ */
+
+// The number of consecutive low temperature errors that can occur
+// before a MINTEMP error is triggered. (Shouldn't be more than 10.)
+//#define MAX_CONSECUTIVE_LOW_TEMPERATURE_ERROR_ALLOWED 0
+
+/**
+ * The number of milliseconds a hotend will preheat before starting to check
+ * the temperature. This value should NOT be set to the time it takes the
+ * hot end to reach the target temperature, but the time it takes to reach
+ * the minimum temperature your thermistor can read. The lower the better/safer.
+ * This shouldn't need to be more than 30 seconds (30000)
+ */
+//#define PREHEAT_TIME_HOTEND_MS 0
+//#define PREHEAT_TIME_BED_MS 0
+
+// @section extruder
+
+/**
+ * Extruder runout prevention.
+ * If the machine is idle and the temperature over MINTEMP
+ * then extrude some filament every couple of SECONDS.
+ */
+//#define EXTRUDER_RUNOUT_PREVENT
+#if ENABLED(EXTRUDER_RUNOUT_PREVENT)
+ #define EXTRUDER_RUNOUT_MINTEMP 190
+ #define EXTRUDER_RUNOUT_SECONDS 30
+ #define EXTRUDER_RUNOUT_SPEED 1500 // (mm/min)
+ #define EXTRUDER_RUNOUT_EXTRUDE 5 // (mm)
+#endif
+
+/**
+ * Hotend Idle Timeout
+ * Prevent filament in the nozzle from charring and causing a critical jam.
+ */
+#define HOTEND_IDLE_TIMEOUT // MRiscoC Disable heaters after timeout
+#if ENABLED(HOTEND_IDLE_TIMEOUT)
+ #define HOTEND_IDLE_TIMEOUT_SEC (10*60) // (seconds) Time without extruder movement to trigger protection // MRiscoC 10 minutes for heaters timeout
+ #define HOTEND_IDLE_MIN_TRIGGER 150 // (°C) Minimum temperature to enable hotend protection // MRiscoC set idle trigger lower than default EXTRUDE_MINTEMP
+ #define HOTEND_IDLE_NOZZLE_TARGET 0 // (°C) Safe temperature for the nozzle after timeout
+ #define HOTEND_IDLE_BED_TARGET 0 // (°C) Safe temperature for the bed after timeout
+#endif
+
+// @section temperature
+
+// Calibration for AD595 / AD8495 sensor to adjust temperature measurements.
+// The final temperature is calculated as (measuredTemp * GAIN) + OFFSET.
+#define TEMP_SENSOR_AD595_OFFSET 0.0
+#define TEMP_SENSOR_AD595_GAIN 1.0
+#define TEMP_SENSOR_AD8495_OFFSET 0.0
+#define TEMP_SENSOR_AD8495_GAIN 1.0
+
+/**
+ * Controller Fan
+ * To cool down the stepper drivers and MOSFETs.
+ *
+ * The fan turns on automatically whenever any driver is enabled and turns
+ * off (or reduces to idle speed) shortly after drivers are turned off.
+ */
+//#define USE_CONTROLLER_FAN
+#if ENABLED(USE_CONTROLLER_FAN)
+ //#define CONTROLLER_FAN_PIN -1 // Set a custom pin for the controller fan
+ //#define CONTROLLER_FAN2_PIN -1 // Set a custom pin for second controller fan
+ //#define CONTROLLER_FAN_USE_Z_ONLY // With this option only the Z axis is considered
+ //#define CONTROLLER_FAN_IGNORE_Z // Ignore Z stepper. Useful when stepper timeout is disabled.
+ #define CONTROLLERFAN_SPEED_MIN 0 // (0-255) Minimum speed. (If set below this value the fan is turned off.)
+ #define CONTROLLERFAN_SPEED_ACTIVE 255 // (0-255) Active speed, used when any motor is enabled
+ #define CONTROLLERFAN_SPEED_IDLE 0 // (0-255) Idle speed, used when motors are disabled
+ #define CONTROLLERFAN_IDLE_TIME 60 // (seconds) Extra time to keep the fan running after disabling motors
+
+ // Use TEMP_SENSOR_BOARD as a trigger for enabling the controller fan
+ //#define CONTROLLER_FAN_MIN_BOARD_TEMP 40 // (°C) Turn on the fan if the board reaches this temperature
+
+ //#define CONTROLLER_FAN_EDITABLE // Enable M710 configurable settings
+ #if ENABLED(CONTROLLER_FAN_EDITABLE)
+ #define CONTROLLER_FAN_MENU // Enable the Controller Fan submenu
+ #endif
+#endif
+
+/**
+ * Fan Kickstart
+ * When part cooling or controller fans first start, run at a speed that
+ * gets it spinning reliably for a short time before setting the requested speed.
+ * (Does not work on Sanguinololu with FAN_SOFT_PWM.)
+ */
+//#define FAN_KICKSTART_TIME 100 // (ms)
+//#define FAN_KICKSTART_POWER 180 // 64-255
+
+// Some coolers may require a non-zero "off" state.
+//#define FAN_OFF_PWM 1
+
+/**
+ * PWM Fan Scaling
+ *
+ * Define the min/max speeds for PWM fans (as set with M106).
+ *
+ * With these options the M106 0-255 value range is scaled to a subset
+ * to ensure that the fan has enough power to spin, or to run lower
+ * current fans with higher current. (e.g., 5V/12V fans with 12V/24V)
+ * Value 0 always turns off the fan.
+ *
+ * Define one or both of these to override the default 0-255 range.
+ */
+#define FAN_MIN_PWM 50 // Ender Configs
+//#define FAN_MAX_PWM 128
+
+/**
+ * Fan Fast PWM
+ *
+ * Combinations of PWM Modes, prescale values and TOP resolutions are used internally
+ * to produce a frequency as close as possible to the desired frequency.
+ *
+ * FAST_PWM_FAN_FREQUENCY
+ * Set this to your desired frequency.
+ * For AVR, if left undefined this defaults to F = F_CPU/(2*255*1)
+ * i.e., F = 31.4kHz on 16MHz microcontrollers or F = 39.2kHz on 20MHz microcontrollers.
+ * For non AVR, if left undefined this defaults to F = 1Khz.
+ * This F value is only to protect the hardware from an absence of configuration
+ * and not to complete it when users are not aware that the frequency must be specifically set to support the target board.
+ *
+ * NOTE: Setting very low frequencies (< 10 Hz) may result in unexpected timer behavior.
+ * Setting very high frequencies can damage your hardware.
+ *
+ * USE_OCR2A_AS_TOP [undefined by default]
+ * Boards that use TIMER2 for PWM have limitations resulting in only a few possible frequencies on TIMER2:
+ * 16MHz MCUs: [62.5kHz, 31.4kHz (default), 7.8kHz, 3.92kHz, 1.95kHz, 977Hz, 488Hz, 244Hz, 60Hz, 122Hz, 30Hz]
+ * 20MHz MCUs: [78.1kHz, 39.2kHz (default), 9.77kHz, 4.9kHz, 2.44kHz, 1.22kHz, 610Hz, 305Hz, 153Hz, 76Hz, 38Hz]
+ * A greater range can be achieved by enabling USE_OCR2A_AS_TOP. But note that this option blocks the use of
+ * PWM on pin OC2A. Only use this option if you don't need PWM on 0C2A. (Check your schematic.)
+ * USE_OCR2A_AS_TOP sacrifices duty cycle control resolution to achieve this broader range of frequencies.
+ */
+//#define FAST_PWM_FAN // Increase the fan PWM frequency. Removes the PWM noise but increases heating in the FET/Arduino
+#if ENABLED(FAST_PWM_FAN)
+ //#define FAST_PWM_FAN_FREQUENCY 31400 // Define here to override the defaults below
+ //#define USE_OCR2A_AS_TOP
+ #ifndef FAST_PWM_FAN_FREQUENCY
+ #ifdef __AVR__
+ #define FAST_PWM_FAN_FREQUENCY ((F_CPU) / (2 * 255 * 1))
+ #else
+ #define FAST_PWM_FAN_FREQUENCY 1000U
+ #endif
+ #endif
+#endif
+
+/**
+ * Use one of the PWM fans as a redundant part-cooling fan
+ */
+//#define REDUNDANT_PART_COOLING_FAN 2 // Index of the fan to sync with FAN 0.
+
+// @section extruder
+
+/**
+ * Extruder cooling fans
+ *
+ * Extruder auto fans automatically turn on when their extruders'
+ * temperatures go above EXTRUDER_AUTO_FAN_TEMPERATURE.
+ *
+ * Your board's pins file specifies the recommended pins. Override those here
+ * or set to -1 to disable completely.
+ *
+ * Multiple extruders can be assigned to the same pin in which case
+ * the fan will turn on when any selected extruder is above the threshold.
+ */
+#define E0_AUTO_FAN_PIN -1 // Ender3V2 Configs
+#define E1_AUTO_FAN_PIN -1
+#define E2_AUTO_FAN_PIN -1
+#define E3_AUTO_FAN_PIN -1
+#define E4_AUTO_FAN_PIN -1
+#define E5_AUTO_FAN_PIN -1
+#define E6_AUTO_FAN_PIN -1
+#define E7_AUTO_FAN_PIN -1
+#define CHAMBER_AUTO_FAN_PIN -1
+#define COOLER_AUTO_FAN_PIN -1
+
+#define EXTRUDER_AUTO_FAN_TEMPERATURE 50
+#define EXTRUDER_AUTO_FAN_SPEED 255 // 255 == full speed
+#define CHAMBER_AUTO_FAN_TEMPERATURE 30
+#define CHAMBER_AUTO_FAN_SPEED 255
+#define COOLER_AUTO_FAN_TEMPERATURE 18
+#define COOLER_AUTO_FAN_SPEED 255
+
+/**
+ * Hotend Cooling Fans tachometers
+ *
+ * Define one or more tachometer pins to enable fan speed
+ * monitoring, and reporting of fan speeds with M123.
+ *
+ * NOTE: Only works with fans up to 7000 RPM.
+ */
+//#define FOURWIRES_FANS // Needed with AUTO_FAN when 4-wire PWM fans are installed
+//#define E0_FAN_TACHO_PIN -1
+//#define E0_FAN_TACHO_PULLUP
+//#define E0_FAN_TACHO_PULLDOWN
+//#define E1_FAN_TACHO_PIN -1
+//#define E1_FAN_TACHO_PULLUP
+//#define E1_FAN_TACHO_PULLDOWN
+//#define E2_FAN_TACHO_PIN -1
+//#define E2_FAN_TACHO_PULLUP
+//#define E2_FAN_TACHO_PULLDOWN
+//#define E3_FAN_TACHO_PIN -1
+//#define E3_FAN_TACHO_PULLUP
+//#define E3_FAN_TACHO_PULLDOWN
+//#define E4_FAN_TACHO_PIN -1
+//#define E4_FAN_TACHO_PULLUP
+//#define E4_FAN_TACHO_PULLDOWN
+//#define E5_FAN_TACHO_PIN -1
+//#define E5_FAN_TACHO_PULLUP
+//#define E5_FAN_TACHO_PULLDOWN
+//#define E6_FAN_TACHO_PIN -1
+//#define E6_FAN_TACHO_PULLUP
+//#define E6_FAN_TACHO_PULLDOWN
+//#define E7_FAN_TACHO_PIN -1
+//#define E7_FAN_TACHO_PULLUP
+//#define E7_FAN_TACHO_PULLDOWN
+
+/**
+ * Part-Cooling Fan Multiplexer
+ *
+ * This feature allows you to digitally multiplex the fan output.
+ * The multiplexer is automatically switched at tool-change.
+ * Set FANMUX[012]_PINs below for up to 2, 4, or 8 multiplexed fans.
+ */
+#define FANMUX0_PIN -1
+#define FANMUX1_PIN -1
+#define FANMUX2_PIN -1
+
+/**
+ * M355 Case Light on-off / brightness
+ */
+//#define CASE_LIGHT_ENABLE
+#if ENABLED(CASE_LIGHT_ENABLE)
+ //#define CASE_LIGHT_PIN 4 // Override the default pin if needed
+ #define INVERT_CASE_LIGHT false // Set true if Case Light is ON when pin is LOW
+ #define CASE_LIGHT_DEFAULT_ON true // Set default power-up state on
+ #define CASE_LIGHT_DEFAULT_BRIGHTNESS 105 // Set default power-up brightness (0-255, requires PWM pin)
+ //#define CASE_LIGHT_NO_BRIGHTNESS // Disable brightness control. Enable for non-PWM lighting.
+ //#define CASE_LIGHT_MAX_PWM 128 // Limit PWM duty cycle (0-255)
+ //#define CASE_LIGHT_MENU // Add Case Light options to the LCD menu
+ #if ENABLED(NEOPIXEL_LED)
+ //#define CASE_LIGHT_USE_NEOPIXEL // Use NeoPixel LED as case light
+ #endif
+ #if EITHER(RGB_LED, RGBW_LED)
+ //#define CASE_LIGHT_USE_RGB_LED // Use RGB / RGBW LED as case light
+ #endif
+ #if EITHER(CASE_LIGHT_USE_NEOPIXEL, CASE_LIGHT_USE_RGB_LED)
+ #define CASE_LIGHT_DEFAULT_COLOR { 255, 255, 255, 255 } // { Red, Green, Blue, White }
+ #endif
+#endif
+
+// @section homing
+
+// If you want endstops to stay on (by default) even when not homing
+// enable this option. Override at any time with M120, M121.
+//#define ENDSTOPS_ALWAYS_ON_DEFAULT
+
+// @section extras
+
+//#define Z_LATE_ENABLE // Enable Z the last moment. Needed if your Z driver overheats.
+
+// Employ an external closed loop controller. Override pins here if needed.
+//#define EXTERNAL_CLOSED_LOOP_CONTROLLER
+#if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
+ //#define CLOSED_LOOP_ENABLE_PIN -1
+ //#define CLOSED_LOOP_MOVE_COMPLETE_PIN -1
+#endif
+
+/**
+ * Dual X Carriage
+ *
+ * This setup has two X carriages that can move independently, each with its own hotend.
+ * The carriages can be used to print an object with two colors or materials, or in
+ * "duplication mode" it can print two identical or X-mirrored objects simultaneously.
+ * The inactive carriage is parked automatically to prevent oozing.
+ * X1 is the left carriage, X2 the right. They park and home at opposite ends of the X axis.
+ * By default the X2 stepper is assigned to the first unused E plug on the board.
+ *
+ * The following Dual X Carriage modes can be selected with M605 S:
+ *
+ * 0 : (FULL_CONTROL) The slicer has full control over both X-carriages and can achieve optimal travel
+ * results as long as it supports dual X-carriages. (M605 S0)
+ *
+ * 1 : (AUTO_PARK) The firmware automatically parks and unparks the X-carriages on tool-change so
+ * that additional slicer support is not required. (M605 S1)
+ *
+ * 2 : (DUPLICATION) The firmware moves the second X-carriage and extruder in synchronization with
+ * the first X-carriage and extruder, to print 2 copies of the same object at the same time.
+ * Set the constant X-offset and temperature differential with M605 S2 X[offs] R[deg] and
+ * follow with M605 S2 to initiate duplicated movement.
+ *
+ * 3 : (MIRRORED) Formbot/Vivedino-inspired mirrored mode in which the second extruder duplicates
+ * the movement of the first except the second extruder is reversed in the X axis.
+ * Set the initial X offset and temperature differential with M605 S2 X[offs] R[deg] and
+ * follow with M605 S3 to initiate mirrored movement.
+ */
+//#define DUAL_X_CARRIAGE
+#if ENABLED(DUAL_X_CARRIAGE)
+ #define X1_MIN_POS X_MIN_POS // Set to X_MIN_POS
+ #define X1_MAX_POS X_BED_SIZE // A max coordinate so the X1 carriage can't hit the parked X2 carriage
+ #define X2_MIN_POS 80 // A min coordinate so the X2 carriage can't hit the parked X1 carriage
+ #define X2_MAX_POS 353 // The max position of the X2 carriage, typically also the home position
+ #define X2_HOME_DIR 1 // Set to 1. The X2 carriage always homes to the max endstop position
+ #define X2_HOME_POS X2_MAX_POS // Default X2 home position. Set to X2_MAX_POS.
+ // NOTE: For Dual X Carriage use M218 T1 Xn to override the X2_HOME_POS.
+ // This allows recalibration of endstops distance without a rebuild.
+ // Remember to set the second extruder's X-offset to 0 in your slicer.
+
+ // This is the default power-up mode which can be changed later using M605 S.
+ #define DEFAULT_DUAL_X_CARRIAGE_MODE DXC_AUTO_PARK_MODE
+
+ // Default x offset in duplication mode (typically set to half print bed width)
+ #define DEFAULT_DUPLICATION_X_OFFSET 100
+
+ // Default action to execute following M605 mode change commands. Typically G28X to apply new mode.
+ //#define EVENT_GCODE_IDEX_AFTER_MODECHANGE "G28X"
+#endif
+
+/**
+ * Multi-Stepper / Multi-Endstop
+ *
+ * When X2_DRIVER_TYPE is defined, this indicates that the X and X2 motors work in tandem.
+ * The following explanations for X also apply to Y and Z multi-stepper setups.
+ * Endstop offsets may be changed by 'M666 X Y Z' and stored to EEPROM.
+ *
+ * - Enable INVERT_X2_VS_X_DIR if the X2 motor requires an opposite DIR signal from X.
+ *
+ * - Enable X_DUAL_ENDSTOPS if the second motor has its own endstop, with adjustable offset.
+ *
+ * - Extra endstops are included in the output of 'M119'.
+ *
+ * - Set X_DUAL_ENDSTOP_ADJUSTMENT to the known error in the X2 endstop.
+ * Applied to the X2 motor on 'G28' / 'G28 X'.
+ * Get the offset by homing X and measuring the error.
+ * Also set with 'M666 X' and stored to EEPROM with 'M500'.
+ *
+ * - Use X2_USE_ENDSTOP to set the endstop plug by name. (_XMIN_, _XMAX_, _YMIN_, _YMAX_, _ZMIN_, _ZMAX_)
+ */
+#if HAS_X2_STEPPER && DISABLED(DUAL_X_CARRIAGE)
+ //#define INVERT_X2_VS_X_DIR // X2 direction signal is the opposite of X
+ //#define X_DUAL_ENDSTOPS // X2 has its own endstop
+ #if ENABLED(X_DUAL_ENDSTOPS)
+ #define X2_USE_ENDSTOP _XMAX_ // X2 endstop board plug. Don't forget to enable USE_*_PLUG.
+ #define X2_ENDSTOP_ADJUSTMENT 0 // X2 offset relative to X endstop
+ #endif
+#endif
+
+#if HAS_DUAL_Y_STEPPERS
+ //#define INVERT_Y2_VS_Y_DIR // Y2 direction signal is the opposite of Y
+ //#define Y_DUAL_ENDSTOPS // Y2 has its own endstop
+ #if ENABLED(Y_DUAL_ENDSTOPS)
+ #define Y2_USE_ENDSTOP _YMAX_ // Y2 endstop board plug. Don't forget to enable USE_*_PLUG.
+ #define Y2_ENDSTOP_ADJUSTMENT 0 // Y2 offset relative to Y endstop
+ #endif
+#endif
+
+//
+// Multi-Z steppers
+//
+#ifdef Z2_DRIVER_TYPE
+ //#define INVERT_Z2_VS_Z_DIR // Z2 direction signal is the opposite of Z
+
+ //#define Z_MULTI_ENDSTOPS // Other Z axes have their own endstops
+ #if ENABLED(Z_MULTI_ENDSTOPS)
+ #define Z2_USE_ENDSTOP _XMAX_ // Z2 endstop board plug. Don't forget to enable USE_*_PLUG.
+ #define Z2_ENDSTOP_ADJUSTMENT 0 // Z2 offset relative to Z endstop
+ #endif
+ #ifdef Z3_DRIVER_TYPE
+ //#define INVERT_Z3_VS_Z_DIR // Z3 direction signal is the opposite of Z
+ #if ENABLED(Z_MULTI_ENDSTOPS)
+ #define Z3_USE_ENDSTOP _YMAX_ // Z3 endstop board plug. Don't forget to enable USE_*_PLUG.
+ #define Z3_ENDSTOP_ADJUSTMENT 0 // Z3 offset relative to Z endstop
+ #endif
+ #endif
+ #ifdef Z4_DRIVER_TYPE
+ //#define INVERT_Z4_VS_Z_DIR // Z4 direction signal is the opposite of Z
+ #if ENABLED(Z_MULTI_ENDSTOPS)
+ #define Z4_USE_ENDSTOP _ZMAX_ // Z4 endstop board plug. Don't forget to enable USE_*_PLUG.
+ #define Z4_ENDSTOP_ADJUSTMENT 0 // Z4 offset relative to Z endstop
+ #endif
+ #endif
+#endif
+
+// Drive the E axis with two synchronized steppers
+//#define E_DUAL_STEPPER_DRIVERS
+#if ENABLED(E_DUAL_STEPPER_DRIVERS)
+ //#define INVERT_E1_VS_E0_DIR // E direction signals are opposites
+#endif
+
+// Activate a solenoid on the active extruder with M380. Disable all with M381.
+// Define SOL0_PIN, SOL1_PIN, etc., for each extruder that has a solenoid.
+//#define EXT_SOLENOID
+
+// @section homing
+
+/**
+ * Homing Procedure
+ * Homing (G28) does an indefinite move towards the endstops to establish
+ * the position of the toolhead relative to the workspace.
+ */
+
+//#define SENSORLESS_BACKOFF_MM { 2, 2, 0 } // (linear=mm, rotational=°) Backoff from endstops before sensorless homing
+
+#define HOMING_BUMP_MM { 5, 5, 2 } // (linear=mm, rotational=°) Backoff from endstops after first bump
+#define HOMING_BUMP_DIVISOR { 2, 2, 4 } // Re-Bump Speed Divisor (Divides the Homing Feedrate)
+
+//#define HOMING_BACKOFF_POST_MM { 2, 2, 2 } // (linear=mm, rotational=°) Backoff from endstops after homing
+//#define XY_COUNTERPART_BACKOFF_MM 0 // (mm) Backoff X after homing Y, and vice-versa
+
+#define QUICK_HOME // If G28 contains XY do a diagonal move first // Ender Configs
+//#define HOME_Y_BEFORE_X // If G28 contains XY home Y before X
+//#define HOME_Z_FIRST // Home Z first. Requires a Z-MIN endstop (not a probe).
+//#define CODEPENDENT_XY_HOMING // If X/Y can't home without homing Y/X first
+
+// @section bltouch
+
+#if ENABLED(BLTOUCH)
+ /**
+ * Either: Use the defaults (recommended) or: For special purposes, use the following DEFINES
+ * Do not activate settings that the probe might not understand. Clones might misunderstand
+ * advanced commands.
+ *
+ * Note: If the probe is not deploying, do a "Reset" and "Self-Test" and then check the
+ * wiring of the BROWN, RED and ORANGE wires.
+ *
+ * Note: If the trigger signal of your probe is not being recognized, it has been very often
+ * because the BLACK and WHITE wires needed to be swapped. They are not "interchangeable"
+ * like they would be with a real switch. So please check the wiring first.
+ *
+ * Settings for all BLTouch and clone probes:
+ */
+
+ // Safety: The probe needs time to recognize the command.
+ // Minimum command delay (ms). Enable and increase if needed.
+ //#define BLTOUCH_DELAY 500
+
+ /**
+ * Settings for BLTOUCH Classic 1.2, 1.3 or BLTouch Smart 1.0, 2.0, 2.2, 3.0, 3.1, and most clones:
+ */
+
+ // Feature: Switch into SW mode after a deploy. It makes the output pulse longer. Can be useful
+ // in special cases, like noisy or filtered input configurations.
+ //#define BLTOUCH_FORCE_SW_MODE
+
+ /**
+ * Settings for BLTouch Smart 3.0 and 3.1
+ * Summary:
+ * - Voltage modes: 5V and OD (open drain - "logic voltage free") output modes
+ * - High-Speed mode
+ * - Disable LCD voltage options
+ */
+
+ /**
+ * Danger: Don't activate 5V mode unless attached to a 5V-tolerant controller!
+ * V3.0 or 3.1: Set default mode to 5V mode at Marlin startup.
+ * If disabled, OD mode is the hard-coded default on 3.0
+ * On startup, Marlin will compare its eeprom to this value. If the selected mode
+ * differs, a mode set eeprom write will be completed at initialization.
+ * Use the option below to force an eeprom write to a V3.1 probe regardless.
+ */
+ //#define BLTOUCH_SET_5V_MODE
+
+ /**
+ * Safety: Activate if connecting a probe with an unknown voltage mode.
+ * V3.0: Set a probe into mode selected above at Marlin startup. Required for 5V mode on 3.0
+ * V3.1: Force a probe with unknown mode into selected mode at Marlin startup ( = Probe EEPROM write )
+ * To preserve the life of the probe, use this once then turn it off and re-flash.
+ */
+ //#define BLTOUCH_FORCE_MODE_SET
+
+ /**
+ * Enable "HIGH SPEED" option for probing.
+ * Danger: Disable if your probe sometimes fails. Only suitable for stable well-adjusted systems.
+ * This feature was designed for Deltabots with very fast Z moves; however, higher speed Cartesians
+ * might be able to use it. If the machine can't raise Z fast enough the BLTouch may go into ALARM.
+ *
+ * Set the default state here, change with 'M401 S' or UI, use M500 to save, M502 to reset.
+ */
+ //#define BLTOUCH_HS_MODE true
+
+ // Safety: Enable voltage mode settings in the LCD menu.
+ //#define BLTOUCH_LCD_VOLTAGE_MENU
+
+#endif // BLTOUCH
+
+// @section extras
+
+/**
+ * Z Steppers Auto-Alignment
+ * Add the G34 command to align multiple Z steppers using a bed probe.
+ */
+//#define Z_STEPPER_AUTO_ALIGN
+#if ENABLED(Z_STEPPER_AUTO_ALIGN)
+ /**
+ * Define probe X and Y positions for Z1, Z2 [, Z3 [, Z4]]
+ * These positions are machine-relative and do not shift with the M206 home offset!
+ * If not defined, probe limits will be used.
+ * Override with 'M422 S X Y'.
+ */
+ //#define Z_STEPPER_ALIGN_XY { { 10, 190 }, { 100, 10 }, { 190, 190 } }
+
+ /**
+ * Orientation for the automatically-calculated probe positions.
+ * Override Z stepper align points with 'M422 S X Y'
+ *
+ * 2 Steppers: (0) (1)
+ * | | 2 |
+ * | 1 2 | |
+ * | | 1 |
+ *
+ * 3 Steppers: (0) (1) (2) (3)
+ * | 3 | 1 | 2 1 | 2 |
+ * | | 3 | | 3 |
+ * | 1 2 | 2 | 3 | 1 |
+ *
+ * 4 Steppers: (0) (1) (2) (3)
+ * | 4 3 | 1 4 | 2 1 | 3 2 |
+ * | | | | |
+ * | 1 2 | 2 3 | 3 4 | 4 1 |
+ */
+ #ifndef Z_STEPPER_ALIGN_XY
+ //#define Z_STEPPERS_ORIENTATION 0
+ #endif
+
+ /**
+ * Z Stepper positions for more rapid convergence in bed alignment.
+ * Requires 3 or 4 Z steppers.
+ *
+ * Define Stepper XY positions for Z1, Z2, Z3... corresponding to the screw
+ * positions in the bed carriage, with one position per Z stepper in stepper
+ * driver order.
+ */
+ //#define Z_STEPPER_ALIGN_STEPPER_XY { { 210.7, 102.5 }, { 152.6, 220.0 }, { 94.5, 102.5 } }
+
+ #ifndef Z_STEPPER_ALIGN_STEPPER_XY
+ // Amplification factor. Used to scale the correction step up or down in case
+ // the stepper (spindle) position is farther out than the test point.
+ #define Z_STEPPER_ALIGN_AMP 1.0 // Use a value > 1.0 NOTE: This may cause instability!
+ #endif
+
+ // On a 300mm bed a 5% grade would give a misalignment of ~1.5cm
+ #define G34_MAX_GRADE 5 // (%) Maximum incline that G34 will handle
+ #define Z_STEPPER_ALIGN_ITERATIONS 5 // Number of iterations to apply during alignment
+ #define Z_STEPPER_ALIGN_ACC 0.02 // Stop iterating early if the accuracy is better than this
+ #define RESTORE_LEVELING_AFTER_G34 // Restore leveling after G34 is done?
+ // After G34, re-home Z (G28 Z) or just calculate it from the last probe heights?
+ // Re-homing might be more precise in reproducing the actual 'G28 Z' homing height, especially on an uneven bed.
+ #define HOME_AFTER_G34
+#endif
+
+//
+// Add the G35 command to read bed corners to help adjust screws. Requires a bed probe.
+//
+//#define ASSISTED_TRAMMING
+#if ENABLED(ASSISTED_TRAMMING)
+
+ // Define from 3 to 9 points to probe.
+ #define TRAMMING_POINT_XY { { 29, 29 }, { 199, 29 }, { 199, 199 }, { 29, 199 } }
+
+ // Define position names for probe points.
+ #define TRAMMING_POINT_NAME_1 "Front-Left"
+ #define TRAMMING_POINT_NAME_2 "Front-Right"
+ #define TRAMMING_POINT_NAME_3 "Back-Right"
+ #define TRAMMING_POINT_NAME_4 "Back-Left"
+
+ #define RESTORE_LEVELING_AFTER_G35 // Enable to restore leveling setup after operation
+ //#define REPORT_TRAMMING_MM // Report Z deviation (mm) for each point relative to the first
+
+ //#define ASSISTED_TRAMMING_WIZARD // Add a Tramming Wizard to the LCD menu
+
+ //#define ASSISTED_TRAMMING_WAIT_POSITION { X_CENTER, Y_CENTER, 30 } // Move the nozzle out of the way for adjustment
+
+ /**
+ * Screw thread:
+ * M3: 30 = Clockwise, 31 = Counter-Clockwise
+ * M4: 40 = Clockwise, 41 = Counter-Clockwise
+ * M5: 50 = Clockwise, 51 = Counter-Clockwise
+ */
+ #define TRAMMING_SCREW_THREAD 40
+
+#endif
+
+// @section motion
+
+/**
+ * Input Shaping -- EXPERIMENTAL
+ *
+ * Zero Vibration (ZV) Input Shaping for X and/or Y movements.
+ *
+ * This option uses a lot of SRAM for the step buffer. The buffer size is
+ * calculated automatically from SHAPING_FREQ_[XY], DEFAULT_AXIS_STEPS_PER_UNIT,
+ * DEFAULT_MAX_FEEDRATE and ADAPTIVE_STEP_SMOOTHING. The default calculation can
+ * be overridden by setting SHAPING_MIN_FREQ and/or SHAPING_MAX_FEEDRATE.
+ * The higher the frequency and the lower the feedrate, the smaller the buffer.
+ * If the buffer is too small at runtime, input shaping will have reduced
+ * effectiveness during high speed movements.
+ *
+ * Tune with M593 D F:
+ *
+ * D Set the zeta/damping factor. If axes (X, Y, etc.) are not specified, set for all axes.
+ * F Set the frequency. If axes (X, Y, etc.) are not specified, set for all axes.
+ * T[map] Input Shaping type, 0:ZV, 1:EI, 2:2H EI (not implemented yet)
+ * X<1> Set the given parameters only for the X axis.
+ * Y<1> Set the given parameters only for the Y axis.
+ */
+//#define INPUT_SHAPING_X
+//#define INPUT_SHAPING_Y
+#if EITHER(INPUT_SHAPING_X, INPUT_SHAPING_Y)
+ #if ENABLED(INPUT_SHAPING_X)
+ #define SHAPING_FREQ_X 40 // (Hz) The default dominant resonant frequency on the X axis.
+ #define SHAPING_ZETA_X 0.15f // Damping ratio of the X axis (range: 0.0 = no damping to 1.0 = critical damping).
+ #endif
+ #if ENABLED(INPUT_SHAPING_Y)
+ #define SHAPING_FREQ_Y 40 // (Hz) The default dominant resonant frequency on the Y axis.
+ #define SHAPING_ZETA_Y 0.15f // Damping ratio of the Y axis (range: 0.0 = no damping to 1.0 = critical damping).
+ #endif
+ //#define SHAPING_MIN_FREQ 20 // By default the minimum of the shaping frequencies. Override to affect SRAM usage.
+ //#define SHAPING_MAX_STEPRATE 10000 // By default the maximum total step rate of the shaped axes. Override to affect SRAM usage.
+ //#define SHAPING_MENU // Add a menu to the LCD to set shaping parameters.
+#endif
+
+#define AXIS_RELATIVE_MODES { false, false, false, false }
+
+// Add a Duplicate option for well-separated conjoined nozzles
+//#define MULTI_NOZZLE_DUPLICATION
+
+// By default stepper drivers require an active-HIGH signal but some high-power drivers require an active-LOW signal to step.
+#define STEP_STATE_X HIGH
+#define STEP_STATE_Y HIGH
+#define STEP_STATE_Z HIGH
+#define STEP_STATE_I HIGH
+#define STEP_STATE_J HIGH
+#define STEP_STATE_K HIGH
+#define STEP_STATE_U HIGH
+#define STEP_STATE_V HIGH
+#define STEP_STATE_W HIGH
+#define STEP_STATE_E HIGH
+
+/**
+ * Idle Stepper Shutdown
+ * Enable DISABLE_INACTIVE_* to shut down axis steppers after an idle period.
+ * The Deactive Time can be overridden with M18 and M84. Set to 0 for No Timeout.
+ */
+#define DEFAULT_STEPPER_DEACTIVE_TIME 120
+#define DISABLE_INACTIVE_X
+#define DISABLE_INACTIVE_Y
+#define DISABLE_INACTIVE_Z // Disable if the nozzle could fall onto your printed part!
+//#define DISABLE_INACTIVE_I
+//#define DISABLE_INACTIVE_J
+//#define DISABLE_INACTIVE_K
+//#define DISABLE_INACTIVE_U
+//#define DISABLE_INACTIVE_V
+//#define DISABLE_INACTIVE_W
+
+// Default Minimum Feedrates for printing and travel moves
+#define DEFAULT_MINIMUMFEEDRATE 0.0 // (mm/s. °/s for rotational-only moves) Minimum feedrate. Set with M205 S.
+#define DEFAULT_MINTRAVELFEEDRATE 0.0 // (mm/s. °/s for rotational-only moves) Minimum travel feedrate. Set with M205 T.
+
+// Minimum time that a segment needs to take as the buffer gets emptied
+#define DEFAULT_MINSEGMENTTIME 20000 // (µs) Set with M205 B.
+
+// Slow down the machine if the lookahead buffer is (by default) half full.
+// Increase the slowdown divisor for larger buffer sizes.
+#define SLOWDOWN
+#if ENABLED(SLOWDOWN)
+ #define SLOWDOWN_DIVISOR 2
+#endif
+
+/**
+ * XY Frequency limit
+ * Reduce resonance by limiting the frequency of small zigzag infill moves.
+ * See https://hydraraptor.blogspot.com/2010/12/frequency-limit.html
+ * Use M201 F G to change limits at runtime.
+ */
+//#define XY_FREQUENCY_LIMIT 10 // (Hz) Maximum frequency of small zigzag infill moves. Set with M201 F.
+#ifdef XY_FREQUENCY_LIMIT
+ #define XY_FREQUENCY_MIN_PERCENT 5 // (percent) Minimum FR percentage to apply. Set with M201 G.
+#endif
+
+// Minimum planner junction speed. Sets the default minimum speed the planner plans for at the end
+// of the buffer and all stops. This should not be much greater than zero and should only be changed
+// if unwanted behavior is observed on a user's machine when running at very slow speeds.
+#define MINIMUM_PLANNER_SPEED 0.05 // (mm/s)
+
+//
+// Backlash Compensation
+// Adds extra movement to axes on direction-changes to account for backlash.
+//
+//#define BACKLASH_COMPENSATION
+#if ENABLED(BACKLASH_COMPENSATION)
+ // Define values for backlash distance and correction.
+ // If BACKLASH_GCODE is enabled these values are the defaults.
+ #define BACKLASH_DISTANCE_MM { 0, 0, 0 } // (linear=mm, rotational=°) One value for each linear axis
+ #define BACKLASH_CORRECTION 0.0 // 0.0 = no correction; 1.0 = full correction
+
+ // Add steps for motor direction changes on CORE kinematics
+ //#define CORE_BACKLASH
+
+ // Set BACKLASH_SMOOTHING_MM to spread backlash correction over multiple segments
+ // to reduce print artifacts. (Enabling this is costly in memory and computation!)
+ //#define BACKLASH_SMOOTHING_MM 3 // (mm)
+
+ // Add runtime configuration and tuning of backlash values (M425)
+ //#define BACKLASH_GCODE
+
+ #if ENABLED(BACKLASH_GCODE)
+ // Measure the Z backlash when probing (G29) and set with "M425 Z"
+ #define MEASURE_BACKLASH_WHEN_PROBING
+
+ #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
+ // When measuring, the probe will move up to BACKLASH_MEASUREMENT_LIMIT
+ // mm away from point of contact in BACKLASH_MEASUREMENT_RESOLUTION
+ // increments while checking for the contact to be broken.
+ #define BACKLASH_MEASUREMENT_LIMIT 0.5 // (mm)
+ #define BACKLASH_MEASUREMENT_RESOLUTION 0.005 // (mm)
+ #define BACKLASH_MEASUREMENT_FEEDRATE Z_PROBE_FEEDRATE_SLOW // (mm/min)
+ #endif
+ #endif
+#endif
+
+/**
+ * Automatic backlash, position, and hotend offset calibration
+ *
+ * Enable G425 to run automatic calibration using an electrically-
+ * conductive cube, bolt, or washer mounted on the bed.
+ *
+ * G425 uses the probe to touch the top and sides of the calibration object
+ * on the bed and measures and/or correct positional offsets, axis backlash
+ * and hotend offsets.
+ *
+ * Note: HOTEND_OFFSET and CALIBRATION_OBJECT_CENTER must be set to within
+ * ±5mm of true values for G425 to succeed.
+ */
+//#define CALIBRATION_GCODE
+#if ENABLED(CALIBRATION_GCODE)
+
+ //#define CALIBRATION_SCRIPT_PRE "M117 Starting Auto-Calibration\nT0\nG28\nG12\nM117 Calibrating..."
+ //#define CALIBRATION_SCRIPT_POST "M500\nM117 Calibration data saved"
+
+ #define CALIBRATION_MEASUREMENT_RESOLUTION 0.01 // mm
+
+ #define CALIBRATION_FEEDRATE_SLOW 60 // mm/min
+ #define CALIBRATION_FEEDRATE_FAST 1200 // mm/min
+ #define CALIBRATION_FEEDRATE_TRAVEL 3000 // mm/min
+
+ // The following parameters refer to the conical section of the nozzle tip.
+ #define CALIBRATION_NOZZLE_TIP_HEIGHT 1.0 // mm
+ #define CALIBRATION_NOZZLE_OUTER_DIAMETER 2.0 // mm
+
+ // Uncomment to enable reporting (required for "G425 V", but consumes PROGMEM).
+ //#define CALIBRATION_REPORTING
+
+ // The true location and dimension the cube/bolt/washer on the bed.
+ #define CALIBRATION_OBJECT_CENTER { 264.0, -22.0, -2.0 } // mm
+ #define CALIBRATION_OBJECT_DIMENSIONS { 10.0, 10.0, 10.0 } // mm
+
+ // Comment out any sides which are unreachable by the probe. For best
+ // auto-calibration results, all sides must be reachable.
+ #define CALIBRATION_MEASURE_RIGHT
+ #define CALIBRATION_MEASURE_FRONT
+ #define CALIBRATION_MEASURE_LEFT
+ #define CALIBRATION_MEASURE_BACK
+
+ //#define CALIBRATION_MEASURE_IMIN
+ //#define CALIBRATION_MEASURE_IMAX
+ //#define CALIBRATION_MEASURE_JMIN
+ //#define CALIBRATION_MEASURE_JMAX
+ //#define CALIBRATION_MEASURE_KMIN
+ //#define CALIBRATION_MEASURE_KMAX
+ //#define CALIBRATION_MEASURE_UMIN
+ //#define CALIBRATION_MEASURE_UMAX
+ //#define CALIBRATION_MEASURE_VMIN
+ //#define CALIBRATION_MEASURE_VMAX
+ //#define CALIBRATION_MEASURE_WMIN
+ //#define CALIBRATION_MEASURE_WMAX
+
+ // Probing at the exact top center only works if the center is flat. If
+ // probing on a screwhead or hollow washer, probe near the edges.
+ //#define CALIBRATION_MEASURE_AT_TOP_EDGES
+
+ // Define the pin to read during calibration
+ #ifndef CALIBRATION_PIN
+ //#define CALIBRATION_PIN -1 // Define here to override the default pin
+ #define CALIBRATION_PIN_INVERTING false // Set to true to invert the custom pin
+ //#define CALIBRATION_PIN_PULLDOWN
+ #define CALIBRATION_PIN_PULLUP
+ #endif
+#endif
+
+/**
+ * Adaptive Step Smoothing increases the resolution of multi-axis moves, particularly at step frequencies
+ * below 1kHz (for AVR) or 10kHz (for ARM), where aliasing between axes in multi-axis moves causes audible
+ * vibration and surface artifacts. The algorithm adapts to provide the best possible step smoothing at the
+ * lowest stepping frequencies.
+ */
+#define ADAPTIVE_STEP_SMOOTHING
+
+/**
+ * Custom Microstepping
+ * Override as-needed for your setup. Up to 3 MS pins are supported.
+ */
+//#define MICROSTEP1 LOW,LOW,LOW
+//#define MICROSTEP2 HIGH,LOW,LOW
+//#define MICROSTEP4 LOW,HIGH,LOW
+//#define MICROSTEP8 HIGH,HIGH,LOW
+//#define MICROSTEP16 LOW,LOW,HIGH
+//#define MICROSTEP32 HIGH,LOW,HIGH
+
+// Microstep settings (Requires a board with pins named X_MS1, X_MS2, etc.)
+#define MICROSTEP_MODES { 16, 16, 16, 16, 16, 16 } // [1,2,4,8,16]
+
+/**
+ * @section stepper motor current
+ *
+ * Some boards have a means of setting the stepper motor current via firmware.
+ *
+ * The power on motor currents are set by:
+ * PWM_MOTOR_CURRENT - used by MINIRAMBO & ULTIMAIN_2
+ * known compatible chips: A4982
+ * DIGIPOT_MOTOR_CURRENT - used by BQ_ZUM_MEGA_3D, RAMBO & SCOOVO_X9H
+ * known compatible chips: AD5206
+ * DAC_MOTOR_CURRENT_DEFAULT - used by PRINTRBOARD_REVF & RIGIDBOARD_V2
+ * known compatible chips: MCP4728
+ * DIGIPOT_I2C_MOTOR_CURRENTS - used by 5DPRINT, AZTEEG_X3_PRO, AZTEEG_X5_MINI_WIFI, MIGHTYBOARD_REVE
+ * known compatible chips: MCP4451, MCP4018
+ *
+ * Motor currents can also be set by M907 - M910 and by the LCD.
+ * M907 - applies to all.
+ * M908 - BQ_ZUM_MEGA_3D, RAMBO, PRINTRBOARD_REVF, RIGIDBOARD_V2 & SCOOVO_X9H
+ * M909, M910 & LCD - only PRINTRBOARD_REVF & RIGIDBOARD_V2
+ */
+//#define PWM_MOTOR_CURRENT { 1300, 1300, 1250 } // Values in milliamps
+//#define DIGIPOT_MOTOR_CURRENT { 135,135,135,135,135 } // Values 0-255 (RAMBO 135 = ~0.75A, 185 = ~1A)
+//#define DAC_MOTOR_CURRENT_DEFAULT { 70, 80, 90, 80 } // Default drive percent - X, Y, Z, E axis
+
+/**
+ * I2C-based DIGIPOTs (e.g., Azteeg X3 Pro)
+ */
+//#define DIGIPOT_MCP4018 // Requires https://github.com/felias-fogg/SlowSoftI2CMaster
+//#define DIGIPOT_MCP4451
+#if EITHER(DIGIPOT_MCP4018, DIGIPOT_MCP4451)
+ #define DIGIPOT_I2C_NUM_CHANNELS 8 // 5DPRINT:4 AZTEEG_X3_PRO:8 MKS_SBASE:5 MIGHTYBOARD_REVE:5
+
+ // Actual motor currents in Amps. The number of entries must match DIGIPOT_I2C_NUM_CHANNELS.
+ // These correspond to the physical drivers, so be mindful if the order is changed.
+ #define DIGIPOT_I2C_MOTOR_CURRENTS { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 } // AZTEEG_X3_PRO
+
+ //#define DIGIPOT_USE_RAW_VALUES // Use DIGIPOT_MOTOR_CURRENT raw wiper values (instead of A4988 motor currents)
+
+ /**
+ * Common slave addresses:
+ *
+ * A (A shifted) B (B shifted) IC
+ * Smoothie 0x2C (0x58) 0x2D (0x5A) MCP4451
+ * AZTEEG_X3_PRO 0x2C (0x58) 0x2E (0x5C) MCP4451
+ * AZTEEG_X5_MINI 0x2C (0x58) 0x2E (0x5C) MCP4451
+ * AZTEEG_X5_MINI_WIFI 0x58 0x5C MCP4451
+ * MIGHTYBOARD_REVE 0x2F (0x5E) MCP4018
+ */
+ //#define DIGIPOT_I2C_ADDRESS_A 0x2C // Unshifted slave address for first DIGIPOT
+ //#define DIGIPOT_I2C_ADDRESS_B 0x2D // Unshifted slave address for second DIGIPOT
+#endif
+
+//===========================================================================
+//=============================Additional Features===========================
+//===========================================================================
+
+// @section lcd
+
+#if HAS_MANUAL_MOVE_MENU
+ #define MANUAL_FEEDRATE { 50*60, 50*60, 4*60, 2*60 } // (mm/min) Feedrates for manual moves along X, Y, Z, E from panel
+ #define FINE_MANUAL_MOVE 0.025 // (mm) Smallest manual move (< 0.1mm) applying to Z on most machines
+ #if IS_ULTIPANEL
+ #define MANUAL_E_MOVES_RELATIVE // Display extruder move distance rather than "position"
+ #define ULTIPANEL_FEEDMULTIPLY // Encoder sets the feedrate multiplier on the Status Screen
+ #endif
+#endif
+
+// Change values more rapidly when the encoder is rotated faster
+#define ENCODER_RATE_MULTIPLIER
+#if ENABLED(ENCODER_RATE_MULTIPLIER)
+ #define ENCODER_5X_STEPS_PER_SEC 30 // Ender Configs
+ #define ENCODER_10X_STEPS_PER_SEC 80 // (steps/s) Encoder rate for 10x speed // Ender Configs
+ #define ENCODER_100X_STEPS_PER_SEC 130 // (steps/s) Encoder rate for 100x speed // Ender Configs
+#endif
+
+// Play a beep when the feedrate is changed from the Status Screen
+//#define BEEP_ON_FEEDRATE_CHANGE
+#if ENABLED(BEEP_ON_FEEDRATE_CHANGE)
+ #define FEEDRATE_CHANGE_BEEP_DURATION 10
+ #define FEEDRATE_CHANGE_BEEP_FREQUENCY 440
+#endif
+
+//
+// LCD Backlight Timeout
+//
+//#define LCD_BACKLIGHT_TIMEOUT_MINS 1 // (minutes) Timeout before turning off the backlight
+
+#if HAS_BED_PROBE && EITHER(HAS_MARLINUI_MENU, HAS_TFT_LVGL_UI)
+ //#define PROBE_OFFSET_WIZARD // Add a Probe Z Offset calibration option to the LCD menu
+ #if ENABLED(PROBE_OFFSET_WIZARD)
+ /**
+ * Enable to init the Probe Z-Offset when starting the Wizard.
+ * Use a height slightly above the estimated nozzle-to-probe Z offset.
+ * For example, with an offset of -5, consider a starting height of -4.
+ */
+ //#define PROBE_OFFSET_WIZARD_START_Z -4.0
+
+ // Set a convenient position to do the calibration (probing point and nozzle/bed-distance)
+ //#define PROBE_OFFSET_WIZARD_XY_POS { X_CENTER, Y_CENTER }
+ #endif
+#endif
+
+#if HAS_MARLINUI_MENU
+
+ #if HAS_BED_PROBE
+ // Add calibration in the Probe Offsets menu to compensate for X-axis twist.
+ //#define X_AXIS_TWIST_COMPENSATION
+ #if ENABLED(X_AXIS_TWIST_COMPENSATION)
+ /**
+ * Enable to init the Probe Z-Offset when starting the Wizard.
+ * Use a height slightly above the estimated nozzle-to-probe Z offset.
+ * For example, with an offset of -5, consider a starting height of -4.
+ */
+ #define XATC_START_Z 0.0
+ #define XATC_MAX_POINTS 3 // Number of points to probe in the wizard
+ #define XATC_Y_POSITION Y_CENTER // (mm) Y position to probe
+ #define XATC_Z_OFFSETS { 0, 0, 0 } // Z offsets for X axis sample points
+ #endif
+
+ // Show Deploy / Stow Probe options in the Motion menu.
+ #define PROBE_DEPLOY_STOW_MENU
+ #endif
+
+ // Include a page of printer information in the LCD Main Menu
+ //#define LCD_INFO_MENU
+ #if ENABLED(LCD_INFO_MENU)
+ //#define LCD_PRINTER_INFO_IS_BOOTSCREEN // Show bootscreen(s) instead of Printer Info pages
+ #endif
+
+ // Add 50/100mm moves to MarlinUI even with a smaller bed
+ //#define LARGE_MOVE_ITEMS
+
+ // BACK menu items keep the highlight at the top
+ //#define TURBO_BACK_MENU_ITEM
+
+ // Insert a menu for preheating at the top level to allow for quick access
+ //#define PREHEAT_SHORTCUT_MENU_ITEM
+
+#endif // HAS_MARLINUI_MENU
+
+#if HAS_DISPLAY
+ #define SOUND_MENU_ITEM // Add a mute option to the LCD menu // MRiscoC Enable Sound Menu Item
+ #define SOUND_ON_DEFAULT // Buzzer/speaker default enabled state
+
+ // The timeout to return to the status screen from sub-menus
+ //#define LCD_TIMEOUT_TO_STATUS 15000 // (ms)
+
+ #if ENABLED(SHOW_BOOTSCREEN)
+ #define BOOTSCREEN_TIMEOUT 4000 // (ms) Total Duration to display the boot screen(s)
+ #if EITHER(HAS_MARLINUI_U8GLIB, TFT_COLOR_UI)
+ #define BOOT_MARLIN_LOGO_SMALL // Show a smaller Marlin logo on the Boot Screen (saving lots of flash)
+ #endif
+ #endif
+
+ // Scroll a longer status message into view
+ #define STATUS_MESSAGE_SCROLLING // MRiscoC Allow scrolling of large status messages
+
+ // Apply a timeout to low-priority status messages
+ #define STATUS_MESSAGE_TIMEOUT_SEC 30 // (seconds) // MRiscoC Enable Status Message Timeout
+
+ // On the Info Screen, display XY with one decimal place when possible
+ //#define LCD_DECIMAL_SMALL_XY
+
+ // Show the E position (filament used) during printing
+ //#define LCD_SHOW_E_TOTAL
+
+ // Display a negative temperature instead of "err"
+ //#define SHOW_TEMPERATURE_BELOW_ZERO
+
+ /**
+ * LED Control Menu
+ * Add LED Control to the LCD menu
+ */
+ //#define LED_CONTROL_MENU
+ #if ENABLED(LED_CONTROL_MENU)
+ #define LED_COLOR_PRESETS // Enable the Preset Color menu option
+ //#define NEO2_COLOR_PRESETS // Enable a second NeoPixel Preset Color menu option
+ #if ENABLED(LED_COLOR_PRESETS)
+ #define LED_USER_PRESET_RED 255 // User defined RED value
+ #define LED_USER_PRESET_GREEN 128 // User defined GREEN value
+ #define LED_USER_PRESET_BLUE 0 // User defined BLUE value
+ #define LED_USER_PRESET_WHITE 255 // User defined WHITE value
+ #define LED_USER_PRESET_BRIGHTNESS 255 // User defined intensity
+ //#define LED_USER_PRESET_STARTUP // Have the printer display the user preset color on startup
+ #endif
+ #if ENABLED(NEO2_COLOR_PRESETS)
+ #define NEO2_USER_PRESET_RED 255 // User defined RED value
+ #define NEO2_USER_PRESET_GREEN 128 // User defined GREEN value
+ #define NEO2_USER_PRESET_BLUE 0 // User defined BLUE value
+ #define NEO2_USER_PRESET_WHITE 255 // User defined WHITE value
+ #define NEO2_USER_PRESET_BRIGHTNESS 255 // User defined intensity
+ //#define NEO2_USER_PRESET_STARTUP // Have the printer display the user preset color on startup for the second strip
+ #endif
+ #endif
+
+#endif // HAS_DISPLAY
+
+// Add 'M73' to set print job progress, overrides Marlin's built-in estimate
+#define SET_PROGRESS_MANUALLY // MRiscoC Allow display feedback of host printing through GCode M73
+#if ENABLED(SET_PROGRESS_MANUALLY)
+ #define SET_PROGRESS_PERCENT // Add 'P' parameter to set percentage done // MRiscoC Allow display feedback of host printing through GCode M73
+ #define SET_REMAINING_TIME // Add 'R' parameter to set remaining time // MRiscoC Allow display feedback of host printing through GCode M73
+ //#define SET_INTERACTION_TIME // Add 'C' parameter to set time until next filament change or other user interaction
+ //#define M73_REPORT // Report M73 values to host
+ #if BOTH(M73_REPORT, SDSUPPORT)
+ #define M73_REPORT_SD_ONLY // Report only when printing from SD
+ #endif
+#endif
+
+// LCD Print Progress options. Multiple times may be displayed in turn.
+#if HAS_DISPLAY && EITHER(SDSUPPORT, SET_PROGRESS_MANUALLY)
+ #define SHOW_PROGRESS_PERCENT // Show print progress percentage (doesn't affect progress bar)
+ #define SHOW_ELAPSED_TIME // Display elapsed printing time (prefix 'E')
+ //#define SHOW_REMAINING_TIME // Display estimated time to completion (prefix 'R')
+ #if ENABLED(SET_INTERACTION_TIME)
+ #define SHOW_INTERACTION_TIME // Display time until next user interaction ('C' = filament change)
+ #endif
+ //#define PRINT_PROGRESS_SHOW_DECIMALS // Show/report progress with decimal digits, not all UIs support this
+
+ #if EITHER(HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL)
+ //#define LCD_PROGRESS_BAR // Show a progress bar on HD44780 LCDs for SD printing
+ #if ENABLED(LCD_PROGRESS_BAR)
+ #define PROGRESS_BAR_BAR_TIME 2000 // (ms) Amount of time to show the bar
+ #define PROGRESS_BAR_MSG_TIME 3000 // (ms) Amount of time to show the status message
+ #define PROGRESS_MSG_EXPIRE 0 // (ms) Amount of time to retain the status message (0=forever)
+ //#define PROGRESS_MSG_ONCE // Show the message for MSG_TIME then clear it
+ //#define LCD_PROGRESS_BAR_TEST // Add a menu item to test the progress bar
+ #endif
+ #endif
+#endif
+
+#if ENABLED(SDSUPPORT)
+ /**
+ * SD Card SPI Speed
+ * May be required to resolve "volume init" errors.
+ *
+ * Enable and set to SPI_HALF_SPEED, SPI_QUARTER_SPEED, or SPI_EIGHTH_SPEED
+ * otherwise full speed will be applied.
+ *
+ * :['SPI_HALF_SPEED', 'SPI_QUARTER_SPEED', 'SPI_EIGHTH_SPEED']
+ */
+ //#define SD_SPI_SPEED SPI_HALF_SPEED
+
+ // The standard SD detect circuit reads LOW when media is inserted and HIGH when empty.
+ // Enable this option and set to HIGH if your SD cards are incorrectly detected.
+ //#define SD_DETECT_STATE HIGH
+
+ //#define SD_IGNORE_AT_STARTUP // Don't mount the SD card when starting up
+ //#define SDCARD_READONLY // Read-only SD card (to save over 2K of flash)
+
+ //#define GCODE_REPEAT_MARKERS // Enable G-code M808 to set repeat markers and do looping
+
+ //#define SD_PROCEDURE_DEPTH 1 // Increase if you need more nested M32 calls // MRiscoC save program memory
+
+ #define SD_FINISHED_STEPPERRELEASE true // Disable steppers when SD Print is finished
+ #define SD_FINISHED_RELEASECOMMAND "M84" // Use "M84XYE" to keep Z enabled so your bed stays in place
+
+ // Reverse SD sort to show "more recent" files first, according to the card's FAT.
+ // Since the FAT gets out of order with usage, SDCARD_SORT_ALPHA is recommended.
+ #define SDCARD_RATHERRECENTFIRST
+
+ #define SD_MENU_CONFIRM_START // Confirm the selected SD file before printing
+
+ //#define NO_SD_AUTOSTART // Remove auto#.g file support completely to save some Flash, SRAM
+ //#define MENU_ADDAUTOSTART // Add a menu option to run auto#.g files
+
+ //#define BROWSE_MEDIA_ON_INSERT // Open the file browser when media is inserted
+
+ //#define MEDIA_MENU_AT_TOP // Force the media menu to be listed on the top of the main menu
+
+ #define EVENT_GCODE_SD_ABORT "G28XY" // G-code to run on SD Abort Print (e.g., "G28XY" or "G27")
+
+ #if ENABLED(PRINTER_EVENT_LEDS)
+ #define PE_LEDS_COMPLETED_TIME (30*60) // (seconds) Time to keep the LED "done" color before restoring normal illumination
+ #endif
+
+ /**
+ * Continue after Power-Loss (Creality3D)
+ *
+ * Store the current state to the SD Card at the start of each layer
+ * during SD printing. If the recovery file is found at boot time, present
+ * an option on the LCD screen to continue the print from the last-known
+ * point in the file.
+ */
+ #define POWER_LOSS_RECOVERY // Ender Configs
+ #if ENABLED(POWER_LOSS_RECOVERY)
+ #define PLR_ENABLED_DEFAULT false // Power Loss Recovery enabled by default. (Set with 'M413 Sn' & M500)
+ //#define BACKUP_POWER_SUPPLY // Backup power / UPS to move the steppers on power loss
+ //#define POWER_LOSS_ZRAISE 2 // (mm) Z axis raise on resume (on power loss with UPS)
+ //#define POWER_LOSS_PIN 44 // Pin to detect power loss. Set to -1 to disable default pin on boards without module.
+ //#define POWER_LOSS_STATE HIGH // State of pin indicating power loss
+ //#define POWER_LOSS_PULLUP // Set pullup / pulldown as appropriate for your sensor
+ //#define POWER_LOSS_PULLDOWN
+ //#define POWER_LOSS_PURGE_LEN 20 // (mm) Length of filament to purge on resume
+ //#define POWER_LOSS_RETRACT_LEN 10 // (mm) Length of filament to retract on fail. Requires backup power.
+
+ // Without a POWER_LOSS_PIN the following option helps reduce wear on the SD card,
+ // especially with "vase mode" printing. Set too high and vases cannot be continued.
+ #define POWER_LOSS_MIN_Z_CHANGE 0.05 // (mm) Minimum Z change before saving power-loss data
+
+ // Enable if Z homing is needed for proper recovery. 99.9% of the time this should be disabled!
+ //#define POWER_LOSS_RECOVER_ZHOME
+ #if ENABLED(POWER_LOSS_RECOVER_ZHOME)
+ //#define POWER_LOSS_ZHOME_POS { 0, 0 } // Safe XY position to home Z while avoiding objects on the bed
+ #endif
+ #endif
+
+ /**
+ * Sort SD file listings in alphabetical order.
+ *
+ * With this option enabled, items on SD cards will be sorted
+ * by name for easier navigation.
+ *
+ * By default...
+ *
+ * - Use the slowest -but safest- method for sorting.
+ * - Folders are sorted to the top.
+ * - The sort key is statically allocated.
+ * - No added G-code (M34) support.
+ * - 40 item sorting limit. (Items after the first 40 are unsorted.)
+ *
+ * SD sorting uses static allocation (as set by SDSORT_LIMIT), allowing the
+ * compiler to calculate the worst-case usage and throw an error if the SRAM
+ * limit is exceeded.
+ *
+ * - SDSORT_USES_RAM provides faster sorting via a static directory buffer.
+ * - SDSORT_USES_STACK does the same, but uses a local stack-based buffer.
+ * - SDSORT_CACHE_NAMES will retain the sorted file listing in RAM. (Expensive!)
+ * - SDSORT_DYNAMIC_RAM only uses RAM when the SD menu is visible. (Use with caution!)
+ */
+ #define SDCARD_SORT_ALPHA // Ender Configs
+
+ // SD Card Sorting options
+ #if ENABLED(SDCARD_SORT_ALPHA)
+ #define SDSORT_LIMIT 50 // Maximum number of sorted items (10-256). Costs 27 bytes each. // MRiscoC Increase number of sorted items
+ #define FOLDER_SORTING -1 // -1=above 0=none 1=below
+ #define SDSORT_GCODE true // Allow turning sorting on/off with LCD and M34 G-code. // MRiscoC Allows disable file sort by M34 g-code
+ #define SDSORT_USES_RAM true // Pre-allocate a static array for faster pre-sorting. // Ender Configs
+ #define SDSORT_USES_STACK false // Prefer the stack for pre-sorting to give back some SRAM. (Negated by next 2 options.)
+ #define SDSORT_CACHE_NAMES true // Keep sorted items in RAM longer for speedy performance. Most expensive option. // Ender Configs
+ #define SDSORT_DYNAMIC_RAM true // Use dynamic allocation (within SD menus). Least expensive option. Set SDSORT_LIMIT before use! // Ender Configs
+ #define SDSORT_CACHE_VFATS 2 // Maximum number of 13-byte VFAT entries to use for sorting.
+ // Note: Only affects SCROLL_LONG_FILENAMES with SDSORT_CACHE_NAMES but not SDSORT_DYNAMIC_RAM.
+ #endif
+
+ // Allow international symbols in long filenames. To display correctly, the
+ // LCD's font must contain the characters. Check your selected LCD language.
+ //#define UTF_FILENAME_SUPPORT
+
+ #define LONG_FILENAME_HOST_SUPPORT // Get the long filename of a file/folder with 'M33 ' and list long filenames with 'M20 L' // MRiscoC Enabled
+ #define LONG_FILENAME_WRITE_SUPPORT // Create / delete files with long filenames via M28, M30, and Binary Transfer Protocol // MRiscoC Enabled
+ //#define M20_TIMESTAMP_SUPPORT // Include timestamps by adding the 'T' flag to M20 commands
+
+ #define SCROLL_LONG_FILENAMES // Scroll long filenames in the SD card menu // MRiscoC Enabled
+
+ //#define SD_ABORT_NO_COOLDOWN // Leave the heaters on after Stop Print (not recommended!)
+
+ /**
+ * Abort SD printing when any endstop is triggered.
+ * This feature is enabled with 'M540 S1' or from the LCD menu.
+ * Endstops must be activated for this option to work.
+ */
+ //#define SD_ABORT_ON_ENDSTOP_HIT
+ #if ENABLED(SD_ABORT_ON_ENDSTOP_HIT)
+ //#define SD_ABORT_ON_ENDSTOP_HIT_GCODE "G28XY" // G-code to run on endstop hit (e.g., "G28XY" or "G27")
+ #endif
+
+ //#define SD_REPRINT_LAST_SELECTED_FILE // On print completion open the LCD Menu and select the same file
+
+ //#define AUTO_REPORT_SD_STATUS // Auto-report media status with 'M27 S'
+
+ /**
+ * Support for USB thumb drives using an Arduino USB Host Shield or
+ * equivalent MAX3421E breakout board. The USB thumb drive will appear
+ * to Marlin as an SD card.
+ *
+ * The MAX3421E can be assigned the same pins as the SD card reader, with
+ * the following pin mapping:
+ *
+ * SCLK, MOSI, MISO --> SCLK, MOSI, MISO
+ * INT --> SD_DETECT_PIN [1]
+ * SS --> SDSS
+ *
+ * [1] On AVR an interrupt-capable pin is best for UHS3 compatibility.
+ */
+ //#define USB_FLASH_DRIVE_SUPPORT
+ #if ENABLED(USB_FLASH_DRIVE_SUPPORT)
+ /**
+ * USB Host Shield Library
+ *
+ * - UHS2 uses no interrupts and has been production-tested
+ * on a LulzBot TAZ Pro with a 32-bit Archim board.
+ *
+ * - UHS3 is newer code with better USB compatibility. But it
+ * is less tested and is known to interfere with Servos.
+ * [1] This requires USB_INTR_PIN to be interrupt-capable.
+ */
+ //#define USE_UHS2_USB
+ //#define USE_UHS3_USB
+
+ #define DISABLE_DUE_SD_MMC // Disable USB Host access to USB Drive to prevent hangs on block access for DUE platform
+
+ /**
+ * Native USB Host supported by some boards (USB OTG)
+ */
+ //#define USE_OTG_USB_HOST
+
+ #if DISABLED(USE_OTG_USB_HOST)
+ #define USB_CS_PIN SDSS
+ #define USB_INTR_PIN SD_DETECT_PIN
+ #endif
+ #endif
+
+ /**
+ * When using a bootloader that supports SD-Firmware-Flashing,
+ * add a menu item to activate SD-FW-Update on the next reboot.
+ *
+ * Requires ATMEGA2560 (Arduino Mega)
+ *
+ * Tested with this bootloader:
+ * https://github.com/FleetProbe/MicroBridge-Arduino-ATMega2560
+ */
+ //#define SD_FIRMWARE_UPDATE
+ #if ENABLED(SD_FIRMWARE_UPDATE)
+ #define SD_FIRMWARE_UPDATE_EEPROM_ADDR 0x1FF
+ #define SD_FIRMWARE_UPDATE_ACTIVE_VALUE 0xF0
+ #define SD_FIRMWARE_UPDATE_INACTIVE_VALUE 0xFF
+ #endif
+
+ /**
+ * Enable this option if you have more than ~3K of unused flash space.
+ * Marlin will embed all settings in the firmware binary as compressed data.
+ * Use 'M503 C' to write the settings out to the SD Card as 'mc.zip'.
+ * See docs/ConfigEmbedding.md for details on how to use 'mc-apply.py'.
+ */
+ //#define CONFIGURATION_EMBEDDING
+
+ // Add an optimized binary file transfer mode, initiated with 'M28 B1'
+ #define BINARY_FILE_TRANSFER // MRiscoC Enabled for easy firmware upgrade
+
+ #if ENABLED(BINARY_FILE_TRANSFER)
+ // Include extra facilities (e.g., 'M20 F') supporting firmware upload via BINARY_FILE_TRANSFER
+ //#define CUSTOM_FIRMWARE_UPLOAD // MRiscoC Enabled for easy firmware upgrade
+ #endif
+
+ /**
+ * Set this option to one of the following (or the board's defaults apply):
+ *
+ * LCD - Use the SD drive in the external LCD controller.
+ * ONBOARD - Use the SD drive on the control board.
+ * CUSTOM_CABLE - Use a custom cable to access the SD (as defined in a pins file).
+ *
+ * :[ 'LCD', 'ONBOARD', 'CUSTOM_CABLE' ]
+ */
+ //#define SDCARD_CONNECTION LCD
+
+ // Enable if SD detect is rendered useless (e.g., by using an SD extender)
+ //#define NO_SD_DETECT
+
+ /**
+ * Multiple volume support - EXPERIMENTAL.
+ * Adds 'M21 Pm' / 'M21 S' / 'M21 U' to mount SD Card / USB Drive.
+ */
+ //#define MULTI_VOLUME
+ #if ENABLED(MULTI_VOLUME)
+ #define VOLUME_SD_ONBOARD
+ #define VOLUME_USB_FLASH_DRIVE
+ #define DEFAULT_VOLUME SV_SD_ONBOARD
+ #define DEFAULT_SHARED_VOLUME SV_USB_FLASH_DRIVE
+ #endif
+
+#endif // SDSUPPORT
+
+/**
+ * By default an onboard SD card reader may be shared as a USB mass-
+ * storage device. This option hides the SD card from the host PC.
+ */
+#define NO_SD_HOST_DRIVE // Disable SD Card access over USB (for security). // Ender Configs
+
+/**
+ * Additional options for Graphical Displays
+ *
+ * Use the optimizations here to improve printing performance,
+ * which can be adversely affected by graphical display drawing,
+ * especially when doing several short moves, and when printing
+ * on DELTA and SCARA machines.
+ *
+ * Some of these options may result in the display lagging behind
+ * controller events, as there is a trade-off between reliable
+ * printing performance versus fast display updates.
+ */
+#if HAS_MARLINUI_U8GLIB
+ // Save many cycles by drawing a hollow frame or no frame on the Info Screen
+ //#define XYZ_NO_FRAME
+ #define XYZ_HOLLOW_FRAME
+
+ // A bigger font is available for edit items. Costs 3120 bytes of flash.
+ // Western only. Not available for Cyrillic, Kana, Turkish, Greek, or Chinese.
+ //#define USE_BIG_EDIT_FONT
+
+ // A smaller font may be used on the Info Screen. Costs 2434 bytes of flash.
+ // Western only. Not available for Cyrillic, Kana, Turkish, Greek, or Chinese.
+ //#define USE_SMALL_INFOFONT
+
+ /**
+ * Graphical Display Sleep
+ *
+ * The U8G library provides sleep / wake functions for SH1106, SSD1306,
+ * SSD1309, and some other DOGM displays.
+ * Enable this option to save energy and prevent OLED pixel burn-in.
+ * Adds the menu item Configuration > LCD Timeout (m) to set a wait period
+ * from 0 (disabled) to 99 minutes.
+ */
+ //#define DISPLAY_SLEEP_MINUTES 2 // (minutes) Timeout before turning off the screen. Set with M255 S.
+
+ /**
+ * ST7920-based LCDs can emulate a 16 x 4 character display using
+ * the ST7920 character-generator for very fast screen updates.
+ * Enable LIGHTWEIGHT_UI to use this special display mode.
+ *
+ * Since LIGHTWEIGHT_UI has limited space, the position and status
+ * message occupy the same line. Set STATUS_EXPIRE_SECONDS to the
+ * length of time to display the status message before clearing.
+ *
+ * Set STATUS_EXPIRE_SECONDS to zero to never clear the status.
+ * This will prevent position updates from being displayed.
+ */
+ #if IS_U8GLIB_ST7920
+ // Enable this option and reduce the value to optimize screen updates.
+ // The normal delay is 10µs. Use the lowest value that still gives a reliable display.
+ //#define DOGM_SPI_DELAY_US 5
+
+ //#define LIGHTWEIGHT_UI
+ #if ENABLED(LIGHTWEIGHT_UI)
+ #define STATUS_EXPIRE_SECONDS 20
+ #endif
+ #endif
+
+ /**
+ * Status (Info) Screen customizations
+ * These options may affect code size and screen render time.
+ * Custom status screens can forcibly override these settings.
+ */
+ //#define STATUS_COMBINE_HEATERS // Use combined heater images instead of separate ones
+ //#define STATUS_HOTEND_NUMBERLESS // Use plain hotend icons instead of numbered ones (with 2+ hotends)
+ #define STATUS_HOTEND_INVERTED // Show solid nozzle bitmaps when heating (Requires STATUS_HOTEND_ANIM for numbered hotends)
+ #define STATUS_HOTEND_ANIM // Use a second bitmap to indicate hotend heating
+ #define STATUS_BED_ANIM // Use a second bitmap to indicate bed heating
+ #define STATUS_CHAMBER_ANIM // Use a second bitmap to indicate chamber heating
+ //#define STATUS_CUTTER_ANIM // Use a second bitmap to indicate spindle / laser active
+ //#define STATUS_COOLER_ANIM // Use a second bitmap to indicate laser cooling
+ //#define STATUS_FLOWMETER_ANIM // Use multiple bitmaps to indicate coolant flow
+ //#define STATUS_ALT_BED_BITMAP // Use the alternative bed bitmap
+ //#define STATUS_ALT_FAN_BITMAP // Use the alternative fan bitmap
+ //#define STATUS_FAN_FRAMES 3 // :[0,1,2,3,4] Number of fan animation frames
+ //#define STATUS_HEAT_PERCENT // Show heating in a progress bar
+ //#define BOOT_MARLIN_LOGO_ANIMATED // Animated Marlin logo. Costs ~3260 (or ~940) bytes of flash.
+
+ // Frivolous Game Options
+ //#define MARLIN_BRICKOUT
+ //#define MARLIN_INVADERS
+ //#define MARLIN_SNAKE
+ //#define GAMES_EASTER_EGG // Add extra blank lines above the "Games" sub-menu
+
+#endif // HAS_MARLINUI_U8GLIB
+
+#if HAS_MARLINUI_U8GLIB || IS_DWIN_MARLINUI
+ #define MENU_HOLLOW_FRAME // Enable to save many cycles by drawing a hollow frame on Menu Screens
+ //#define OVERLAY_GFX_REVERSE // Swap the CW/CCW indicators in the graphics overlay
+#endif
+
+//
+// Additional options for DGUS / DWIN displays
+//
+#if HAS_DGUS_LCD
+ #define LCD_BAUDRATE 115200
+
+ #define DGUS_RX_BUFFER_SIZE 128
+ #define DGUS_TX_BUFFER_SIZE 48
+ //#define SERIAL_STATS_RX_BUFFER_OVERRUNS // Fix Rx overrun situation (Currently only for AVR)
+
+ #define DGUS_UPDATE_INTERVAL_MS 500 // (ms) Interval between automatic screen updates
+
+ #if DGUS_UI_IS(FYSETC, MKS, HIPRECY)
+ #define DGUS_PRINT_FILENAME // Display the filename during printing
+ #define DGUS_PREHEAT_UI // Display a preheat screen during heatup
+
+ #if DGUS_UI_IS(FYSETC, MKS)
+ //#define DGUS_UI_MOVE_DIS_OPTION // Disabled by default for FYSETC and MKS
+ #else
+ #define DGUS_UI_MOVE_DIS_OPTION // Enabled by default for UI_HIPRECY
+ #endif
+
+ #define DGUS_FILAMENT_LOADUNLOAD
+ #if ENABLED(DGUS_FILAMENT_LOADUNLOAD)
+ #define DGUS_FILAMENT_PURGE_LENGTH 10
+ #define DGUS_FILAMENT_LOAD_LENGTH_PER_TIME 0.5 // (mm) Adjust in proportion to DGUS_UPDATE_INTERVAL_MS
+ #endif
+
+ #define DGUS_UI_WAITING // Show a "waiting" screen between some screens
+ #if ENABLED(DGUS_UI_WAITING)
+ #define DGUS_UI_WAITING_STATUS 10
+ #define DGUS_UI_WAITING_STATUS_PERIOD 8 // Increase to slower waiting status looping
+ #endif
+ #endif
+#endif // HAS_DGUS_LCD
+
+//
+// Additional options for AnyCubic Chiron TFT displays
+//
+#if ENABLED(ANYCUBIC_LCD_CHIRON)
+ // By default the type of panel is automatically detected.
+ // Enable one of these options if you know the panel type.
+ //#define CHIRON_TFT_STANDARD
+ //#define CHIRON_TFT_NEW
+
+ // Enable the longer Anycubic powerup startup tune
+ //#define AC_DEFAULT_STARTUP_TUNE
+
+ /**
+ * Display Folders
+ * By default the file browser lists all G-code files (including those in subfolders) in a flat list.
+ * Enable this option to display a hierarchical file browser.
+ *
+ * NOTES:
+ * - Without this option it helps to enable SDCARD_SORT_ALPHA so files are sorted before/after folders.
+ * - When used with the "new" panel, folder names will also have '.gcode' appended to their names.
+ * This hack is currently required to force the panel to show folders.
+ */
+ #define AC_SD_FOLDER_VIEW
+#endif
+
+//
+// Specify additional languages for the UI. Default specified by LCD_LANGUAGE.
+//
+#if ANY(DOGLCD, TFT_COLOR_UI, TOUCH_UI_FTDI_EVE, IS_DWIN_MARLINUI)
+ //#define LCD_LANGUAGE_2 fr
+ //#define LCD_LANGUAGE_3 de
+ //#define LCD_LANGUAGE_4 es
+ //#define LCD_LANGUAGE_5 it
+ #ifdef LCD_LANGUAGE_2
+ //#define LCD_LANGUAGE_AUTO_SAVE // Automatically save language to EEPROM on change
+ #endif
+#endif
+
+//
+// Touch UI for the FTDI Embedded Video Engine (EVE)
+//
+#if ENABLED(TOUCH_UI_FTDI_EVE)
+ // Display board used
+ //#define LCD_FTDI_VM800B35A // FTDI 3.5" with FT800 (320x240)
+ //#define LCD_4DSYSTEMS_4DLCD_FT843 // 4D Systems 4.3" (480x272)
+ //#define LCD_HAOYU_FT800CB // Haoyu with 4.3" or 5" (480x272)
+ //#define LCD_HAOYU_FT810CB // Haoyu with 5" (800x480)
+ //#define LCD_LULZBOT_CLCD_UI // LulzBot Color LCD UI
+ //#define LCD_FYSETC_TFT81050 // FYSETC with 5" (800x480)
+ //#define LCD_EVE3_50G // Matrix Orbital 5.0", 800x480, BT815
+ //#define LCD_EVE2_50G // Matrix Orbital 5.0", 800x480, FT813
+
+ // Correct the resolution if not using the stock TFT panel.
+ //#define TOUCH_UI_320x240
+ //#define TOUCH_UI_480x272
+ //#define TOUCH_UI_800x480
+
+ // Mappings for boards with a standard RepRapDiscount Display connector
+ //#define AO_EXP1_PINMAP // LulzBot CLCD UI EXP1 mapping
+ //#define AO_EXP2_PINMAP // LulzBot CLCD UI EXP2 mapping
+ //#define CR10_TFT_PINMAP // Rudolph Riedel's CR10 pin mapping
+ //#define S6_TFT_PINMAP // FYSETC S6 pin mapping
+ //#define F6_TFT_PINMAP // FYSETC F6 pin mapping
+
+ //#define OTHER_PIN_LAYOUT // Define pins manually below
+ #if ENABLED(OTHER_PIN_LAYOUT)
+ // Pins for CS and MOD_RESET (PD) must be chosen
+ #define CLCD_MOD_RESET 9
+ #define CLCD_SPI_CS 10
+
+ // If using software SPI, specify pins for SCLK, MOSI, MISO
+ //#define CLCD_USE_SOFT_SPI
+ #if ENABLED(CLCD_USE_SOFT_SPI)
+ #define CLCD_SOFT_SPI_MOSI 11
+ #define CLCD_SOFT_SPI_MISO 12
+ #define CLCD_SOFT_SPI_SCLK 13
+ #endif
+ #endif
+
+ // Display Orientation. An inverted (i.e. upside-down) display
+ // is supported on the FT800. The FT810 and beyond also support
+ // portrait and mirrored orientations.
+ //#define TOUCH_UI_INVERTED
+ //#define TOUCH_UI_PORTRAIT
+ //#define TOUCH_UI_MIRRORED
+
+ // UTF8 processing and rendering.
+ // Unsupported characters are shown as '?'.
+ //#define TOUCH_UI_USE_UTF8
+ #if ENABLED(TOUCH_UI_USE_UTF8)
+ // Western accents support. These accented characters use
+ // combined bitmaps and require relatively little storage.
+ #define TOUCH_UI_UTF8_WESTERN_CHARSET
+ #if ENABLED(TOUCH_UI_UTF8_WESTERN_CHARSET)
+ // Additional character groups. These characters require
+ // full bitmaps and take up considerable storage:
+ //#define TOUCH_UI_UTF8_SUPERSCRIPTS // ¹ ² ³
+ //#define TOUCH_UI_UTF8_COPYRIGHT // © ®
+ //#define TOUCH_UI_UTF8_GERMANIC // ß
+ //#define TOUCH_UI_UTF8_SCANDINAVIAN // Æ Ð Ø Þ æ ð ø þ
+ //#define TOUCH_UI_UTF8_PUNCTUATION // « » ¿ ¡
+ //#define TOUCH_UI_UTF8_CURRENCY // ¢ £ ¤ ¥
+ //#define TOUCH_UI_UTF8_ORDINALS // º ª
+ //#define TOUCH_UI_UTF8_MATHEMATICS // ± × ÷
+ //#define TOUCH_UI_UTF8_FRACTIONS // ¼ ½ ¾
+ //#define TOUCH_UI_UTF8_SYMBOLS // µ ¶ ¦ § ¬
+ #endif
+
+ // Cyrillic character set, costs about 27KiB of flash
+ //#define TOUCH_UI_UTF8_CYRILLIC_CHARSET
+ #endif
+
+ // Use a smaller font when labels don't fit buttons
+ #define TOUCH_UI_FIT_TEXT
+
+ // Use a numeric passcode for "Screen lock" keypad.
+ // (recommended for smaller displays)
+ //#define TOUCH_UI_PASSCODE
+
+ // Output extra debug info for Touch UI events
+ //#define TOUCH_UI_DEBUG
+
+ // Developer menu (accessed by touching "About Printer" copyright text)
+ //#define TOUCH_UI_DEVELOPER_MENU
+#endif
+
+//
+// Classic UI Options
+//
+#if TFT_SCALED_DOGLCD
+ //#define TFT_MARLINUI_COLOR 0xFFFF // White
+ //#define TFT_MARLINBG_COLOR 0x0000 // Black
+ //#define TFT_DISABLED_COLOR 0x0003 // Almost black
+ //#define TFT_BTCANCEL_COLOR 0xF800 // Red
+ //#define TFT_BTARROWS_COLOR 0xDEE6 // 11011 110111 00110 Yellow
+ //#define TFT_BTOKMENU_COLOR 0x145F // 00010 100010 11111 Cyan
+#endif
+
+//
+// ADC Button Debounce
+//
+#if HAS_ADC_BUTTONS
+ #define ADC_BUTTON_DEBOUNCE_DELAY 16 // Increase if buttons bounce or repeat too fast
+#endif
+
+// @section safety
+
+/**
+ * The watchdog hardware timer will do a reset and disable all outputs
+ * if the firmware gets too overloaded to read the temperature sensors.
+ *
+ * If you find that watchdog reboot causes your AVR board to hang forever,
+ * enable WATCHDOG_RESET_MANUAL to use a custom timer instead of WDTO.
+ * NOTE: This method is less reliable as it can only catch hangups while
+ * interrupts are enabled.
+ */
+#define USE_WATCHDOG
+#if ENABLED(USE_WATCHDOG)
+ //#define WATCHDOG_RESET_MANUAL
+#endif
+
+// @section lcd
+
+/**
+ * Babystepping enables movement of the axes by tiny increments without changing
+ * the current position values. This feature is used primarily to adjust the Z
+ * axis in the first layer of a print in real-time.
+ *
+ * Warning: Does not respect endstops!
+ */
+#define BABYSTEPPING // Ender Configs
+#if ENABLED(BABYSTEPPING)
+ //#define INTEGRATED_BABYSTEPPING // EXPERIMENTAL integration of babystepping into the Stepper ISR
+ #define BABYSTEP_WITHOUT_HOMING // MRiscoC Enabled BbS without home
+ #define BABYSTEP_ALWAYS_AVAILABLE // Allow babystepping at all times (not just during movement). // MRiscoC Active BbS always
+ //#define BABYSTEP_XY // Also enable X/Y Babystepping. Not supported on DELTA!
+ #define BABYSTEP_INVERT_Z false // Change if Z babysteps should go the other way
+ //#define BABYSTEP_MILLIMETER_UNITS // Specify BABYSTEP_MULTIPLICATOR_(XY|Z) in mm instead of micro-steps
+ #define BABYSTEP_MULTIPLICATOR_Z 40 // (steps or mm) Steps or millimeter distance for each Z babystep // Ender Configs
+ #define BABYSTEP_MULTIPLICATOR_XY 1 // (steps or mm) Steps or millimeter distance for each XY babystep
+
+ //#define DOUBLECLICK_FOR_Z_BABYSTEPPING // Double-click on the Status Screen for Z Babystepping.
+ #if ENABLED(DOUBLECLICK_FOR_Z_BABYSTEPPING)
+ #define DOUBLECLICK_MAX_INTERVAL 1250 // Maximum interval between clicks, in milliseconds.
+ // Note: Extra time may be added to mitigate controller latency.
+ //#define MOVE_Z_WHEN_IDLE // Jump to the move Z menu on doubleclick when printer is idle.
+ #if ENABLED(MOVE_Z_WHEN_IDLE)
+ #define MOVE_Z_IDLE_MULTIPLICATOR 1 // Multiply 1mm by this factor for the move step size.
+ #endif
+ #endif
+
+ //#define BABYSTEP_DISPLAY_TOTAL // Display total babysteps since last G28
+
+ //#define BABYSTEP_ZPROBE_OFFSET // Combine M851 Z and Babystepping
+ #if ENABLED(BABYSTEP_ZPROBE_OFFSET)
+ //#define BABYSTEP_HOTEND_Z_OFFSET // For multiple hotends, babystep relative Z offsets
+ //#define BABYSTEP_GFX_OVERLAY // Enable graphical overlay on Z-offset editor
+ #endif
+#endif
+
+// @section extruder
+
+/**
+ * Linear Pressure Control v1.5
+ *
+ * Assumption: advance [steps] = k * (delta velocity [steps/s])
+ * K=0 means advance disabled.
+ *
+ * NOTE: K values for LIN_ADVANCE 1.5 differ from earlier versions!
+ *
+ * Set K around 0.22 for 3mm PLA Direct Drive with ~6.5cm between the drive gear and heatbreak.
+ * Larger K values will be needed for flexible filament and greater distances.
+ * If this algorithm produces a higher speed offset than the extruder can handle (compared to E jerk)
+ * print acceleration will be reduced during the affected moves to keep within the limit.
+ *
+ * See https://marlinfw.org/docs/features/lin_advance.html for full instructions.
+ */
+//#define LIN_ADVANCE
+#if ENABLED(LIN_ADVANCE)
+ #if ENABLED(DISTINCT_E_FACTORS)
+ #define ADVANCE_K { 0.22 } // (mm) Compression length per 1mm/s extruder speed, per extruder
+ #else
+ #define ADVANCE_K 0.22 // (mm) Compression length applying to all extruders
+ #endif
+ //#define ADVANCE_K_EXTRA // Add a second linear advance constant, configurable with M900 L.
+ //#define LA_DEBUG // Print debug information to serial during operation. Disable for production use.
+ //#define ALLOW_LOW_EJERK // Allow a DEFAULT_EJERK value of <10. Recommended for direct drive hotends.
+ //#define EXPERIMENTAL_I2S_LA // Allow I2S_STEPPER_STREAM to be used with LA. Performance degrades as the LA step rate reaches ~20kHz.
+#endif
+
+// @section leveling
+
+/**
+ * Use Safe Bed Leveling coordinates to move axes to a useful position before bed probing.
+ * For example, after homing a rotational axis the Z probe might not be perpendicular to the bed.
+ * Choose values the orient the bed horizontally and the Z-probe vertically.
+ */
+//#define SAFE_BED_LEVELING_START_X 0.0
+//#define SAFE_BED_LEVELING_START_Y 0.0
+//#define SAFE_BED_LEVELING_START_Z 0.0
+//#define SAFE_BED_LEVELING_START_I 0.0
+//#define SAFE_BED_LEVELING_START_J 0.0
+//#define SAFE_BED_LEVELING_START_K 0.0
+//#define SAFE_BED_LEVELING_START_U 0.0
+//#define SAFE_BED_LEVELING_START_V 0.0
+//#define SAFE_BED_LEVELING_START_W 0.0
+
+/**
+ * Points to probe for all 3-point Leveling procedures.
+ * Override if the automatically selected points are inadequate.
+ */
+#if EITHER(AUTO_BED_LEVELING_3POINT, AUTO_BED_LEVELING_UBL)
+ //#define PROBE_PT_1_X 15
+ //#define PROBE_PT_1_Y 180
+ //#define PROBE_PT_2_X 15
+ //#define PROBE_PT_2_Y 20
+ //#define PROBE_PT_3_X 170
+ //#define PROBE_PT_3_Y 20
+#endif
+
+/**
+ * Probing Margins
+ *
+ * Override PROBING_MARGIN for each side of the build plate
+ * Useful to get probe points to exact positions on targets or
+ * to allow leveling to avoid plate clamps on only specific
+ * sides of the bed. With NOZZLE_AS_PROBE negative values are
+ * allowed, to permit probing outside the bed.
+ *
+ * If you are replacing the prior *_PROBE_BED_POSITION options,
+ * LEFT and FRONT values in most cases will map directly over
+ * RIGHT and REAR would be the inverse such as
+ * (X/Y_BED_SIZE - RIGHT/BACK_PROBE_BED_POSITION)
+ *
+ * This will allow all positions to match at compilation, however
+ * should the probe position be modified with M851XY then the
+ * probe points will follow. This prevents any change from causing
+ * the probe to be unable to reach any points.
+ */
+#if PROBE_SELECTED && !IS_KINEMATIC
+ //#define PROBING_MARGIN_LEFT PROBING_MARGIN
+ //#define PROBING_MARGIN_RIGHT PROBING_MARGIN
+ //#define PROBING_MARGIN_FRONT PROBING_MARGIN
+ //#define PROBING_MARGIN_BACK PROBING_MARGIN
+#endif
+
+#if EITHER(MESH_BED_LEVELING, AUTO_BED_LEVELING_UBL)
+ // Override the mesh area if the automatic (max) area is too large
+ #define MESH_MIN_X MESH_INSET
+ #define MESH_MIN_Y MESH_INSET
+ #define MESH_MAX_X X_BED_SIZE - (MESH_INSET)
+ #define MESH_MAX_Y Y_BED_SIZE - (MESH_INSET)
+#endif
+
+#if BOTH(AUTO_BED_LEVELING_UBL, EEPROM_SETTINGS)
+ //#define OPTIMIZED_MESH_STORAGE // Store mesh with less precision to save EEPROM space
+#endif
+
+/**
+ * Repeatedly attempt G29 leveling until it succeeds.
+ * Stop after G29_MAX_RETRIES attempts.
+ */
+//#define G29_RETRY_AND_RECOVER
+#if ENABLED(G29_RETRY_AND_RECOVER)
+ #define G29_MAX_RETRIES 3
+ #define G29_HALT_ON_FAILURE
+ /**
+ * Specify the GCODE commands that will be executed when leveling succeeds,
+ * between attempts, and after the maximum number of retries have been tried.
+ */
+ #define G29_SUCCESS_COMMANDS "M117 Bed leveling done."
+ #define G29_RECOVER_COMMANDS "M117 Probe failed. Rewiping.\nG28\nG12 P0 S12 T0"
+ #define G29_FAILURE_COMMANDS "M117 Bed leveling failed.\nG0 Z10\nM300 P25 S880\nM300 P50 S0\nM300 P25 S880\nM300 P50 S0\nM300 P25 S880\nM300 P50 S0\nG4 S1"
+
+#endif
+
+/**
+ * Thermal Probe Compensation
+ *
+ * Adjust probe measurements to compensate for distortion associated with the temperature
+ * of the probe, bed, and/or hotend.
+ * Use G76 to automatically calibrate this feature for probe and bed temperatures.
+ * (Extruder temperature/offset values must be calibrated manually.)
+ * Use M871 to set temperature/offset values manually.
+ * For more details see https://marlinfw.org/docs/features/probe_temp_compensation.html
+ */
+//#define PTC_PROBE // Compensate based on probe temperature
+//#define PTC_BED // Compensate based on bed temperature
+//#define PTC_HOTEND // Compensate based on hotend temperature
+
+#if ANY(PTC_PROBE, PTC_BED, PTC_HOTEND)
+ /**
+ * If the probe is outside the defined range, use linear extrapolation with the closest
+ * point and the point with index PTC_LINEAR_EXTRAPOLATION. e.g., If set to 4 it will use the
+ * linear extrapolation between data[0] and data[4] for values below PTC_PROBE_START.
+ */
+ //#define PTC_LINEAR_EXTRAPOLATION 4
+
+ #if ENABLED(PTC_PROBE)
+ // Probe temperature calibration generates a table of values starting at PTC_PROBE_START
+ // (e.g., 30), in steps of PTC_PROBE_RES (e.g., 5) with PTC_PROBE_COUNT (e.g., 10) samples.
+ #define PTC_PROBE_START 30 // (°C)
+ #define PTC_PROBE_RES 5 // (°C)
+ #define PTC_PROBE_COUNT 10
+ #define PTC_PROBE_ZOFFS { 0 } // (µm) Z adjustments per sample
+ #endif
+
+ #if ENABLED(PTC_BED)
+ // Bed temperature calibration builds a similar table.
+ #define PTC_BED_START 60 // (°C)
+ #define PTC_BED_RES 5 // (°C)
+ #define PTC_BED_COUNT 10
+ #define PTC_BED_ZOFFS { 0 } // (µm) Z adjustments per sample
+ #endif
+
+ #if ENABLED(PTC_HOTEND)
+ // Note: There is no automatic calibration for the hotend. Use M871.
+ #define PTC_HOTEND_START 180 // (°C)
+ #define PTC_HOTEND_RES 5 // (°C)
+ #define PTC_HOTEND_COUNT 20
+ #define PTC_HOTEND_ZOFFS { 0 } // (µm) Z adjustments per sample
+ #endif
+
+ // G76 options
+ #if BOTH(PTC_PROBE, PTC_BED)
+ // Park position to wait for probe cooldown
+ #define PTC_PARK_POS { 0, 0, 100 }
+
+ // Probe position to probe and wait for probe to reach target temperature
+ //#define PTC_PROBE_POS { 12.0f, 7.3f } // Example: MK52 magnetic heatbed
+ #define PTC_PROBE_POS { 90, 100 }
+
+ // The temperature the probe should be at while taking measurements during
+ // bed temperature calibration.
+ #define PTC_PROBE_TEMP 30 // (°C)
+
+ // Height above Z=0.0 to raise the nozzle. Lowering this can help the probe to heat faster.
+ // Note: The Z=0.0 offset is determined by the probe Z offset (e.g., as set with M851 Z).
+ #define PTC_PROBE_HEATING_OFFSET 0.5
+ #endif
+#endif // PTC_PROBE || PTC_BED || PTC_HOTEND
+
+// @section extras
+
+//
+// G60/G61 Position Save and Return
+//
+//#define SAVED_POSITIONS 1 // Each saved position slot costs 12 bytes
+
+//
+// G2/G3 Arc Support
+//
+#define ARC_SUPPORT // Requires ~3226 bytes
+#if ENABLED(ARC_SUPPORT)
+ #define MIN_ARC_SEGMENT_MM 0.1 // (mm) Minimum length of each arc segment
+ #define MAX_ARC_SEGMENT_MM 1.0 // (mm) Maximum length of each arc segment
+ #define MIN_CIRCLE_SEGMENTS 72 // Minimum number of segments in a complete circle
+ //#define ARC_SEGMENTS_PER_SEC 50 // Use the feedrate to choose the segment length
+ #define N_ARC_CORRECTION 25 // Number of interpolated segments between corrections
+ #define ARC_P_CIRCLES // Enable the 'P' parameter to specify complete circles // MRiscoC Enabled
+ //#define SF_ARC_FIX // Enable only if using SkeinForge with "Arc Point" fillet procedure
+#endif
+
+// G5 Bézier Curve Support with XYZE destination and IJPQ offsets
+//#define BEZIER_CURVE_SUPPORT // Requires ~2666 bytes
+
+#if EITHER(ARC_SUPPORT, BEZIER_CURVE_SUPPORT)
+ //#define CNC_WORKSPACE_PLANES // Allow G2/G3/G5 to operate in XY, ZX, or YZ planes
+#endif
+
+/**
+ * Direct Stepping
+ *
+ * Comparable to the method used by Klipper, G6 direct stepping significantly
+ * reduces motion calculations, increases top printing speeds, and results in
+ * less step aliasing by calculating all motions in advance.
+ * Preparing your G-code: https://github.com/colinrgodsey/step-daemon
+ */
+//#define DIRECT_STEPPING
+
+/**
+ * G38 Probe Target
+ *
+ * This option adds G38.2 and G38.3 (probe towards target)
+ * and optionally G38.4 and G38.5 (probe away from target).
+ * Set MULTIPLE_PROBING for G38 to probe more than once.
+ */
+//#define G38_PROBE_TARGET
+#if ENABLED(G38_PROBE_TARGET)
+ //#define G38_PROBE_AWAY // Include G38.4 and G38.5 to probe away from target
+ #define G38_MINIMUM_MOVE 0.0275 // (mm) Minimum distance that will produce a move.
+#endif
+
+// Moves (or segments) with fewer steps than this will be joined with the next move
+#define MIN_STEPS_PER_SEGMENT 4 // MRiscoC Increase little movements accuracy
+
+/**
+ * Minimum delay before and after setting the stepper DIR (in ns)
+ * 0 : No delay (Expect at least 10µS since one Stepper ISR must transpire)
+ * 20 : Minimum for TMC2xxx drivers
+ * 200 : Minimum for A4988 drivers
+ * 400 : Minimum for A5984 drivers
+ * 500 : Minimum for LV8729 drivers (guess, no info in datasheet)
+ * 650 : Minimum for DRV8825 drivers
+ * 1500 : Minimum for TB6600 drivers (guess, no info in datasheet)
+ * 15000 : Minimum for TB6560 drivers (guess, no info in datasheet)
+ *
+ * Override the default value based on the driver type set in Configuration.h.
+ */
+//#define MINIMUM_STEPPER_POST_DIR_DELAY 650
+//#define MINIMUM_STEPPER_PRE_DIR_DELAY 650
+
+/**
+ * Minimum stepper driver pulse width (in µs)
+ * 0 : Smallest possible width the MCU can produce, compatible with TMC2xxx drivers
+ * 0 : Minimum 500ns for LV8729, adjusted in stepper.h
+ * 1 : Minimum for A4988 and A5984 stepper drivers
+ * 2 : Minimum for DRV8825 stepper drivers
+ * 3 : Minimum for TB6600 stepper drivers
+ * 30 : Minimum for TB6560 stepper drivers
+ *
+ * Override the default value based on the driver type set in Configuration.h.
+ */
+//#define MINIMUM_STEPPER_PULSE 2
+
+/**
+ * Maximum stepping rate (in Hz) the stepper driver allows
+ * If undefined, defaults to 1MHz / (2 * MINIMUM_STEPPER_PULSE)
+ * 5000000 : Maximum for TMC2xxx stepper drivers
+ * 1000000 : Maximum for LV8729 stepper driver
+ * 500000 : Maximum for A4988 stepper driver
+ * 250000 : Maximum for DRV8825 stepper driver
+ * 150000 : Maximum for TB6600 stepper driver
+ * 15000 : Maximum for TB6560 stepper driver
+ *
+ * Override the default value based on the driver type set in Configuration.h.
+ */
+//#define MAXIMUM_STEPPER_RATE 250000
+
+// @section temperature
+
+// Control heater 0 and heater 1 in parallel.
+//#define HEATERS_PARALLEL
+
+//===========================================================================
+//================================= Buffers =================================
+//===========================================================================
+
+// @section motion
+
+// The number of linear moves that can be in the planner at once.
+// The value of BLOCK_BUFFER_SIZE must be a power of 2 (e.g., 8, 16, 32)
+#if BOTH(SDSUPPORT, DIRECT_STEPPING)
+ #define BLOCK_BUFFER_SIZE 8
+#elif ENABLED(SDSUPPORT)
+ #define BLOCK_BUFFER_SIZE 16
+#else
+ #define BLOCK_BUFFER_SIZE 16
+#endif
+
+// @section serial
+
+// The ASCII buffer for serial input
+#define MAX_CMD_SIZE 96
+#define BUFSIZE 16 // MRiscoC Increase buffer for Octoprint
+
+// Transmission to Host Buffer Size
+// To save 386 bytes of flash (and TX_BUFFER_SIZE+3 bytes of RAM) set to 0.
+// To buffer a simple "ok" you need 4 bytes.
+// For ADVANCED_OK (M105) you need 32 bytes.
+// For debug-echo: 128 bytes for the optimal speed.
+// Other output doesn't need to be that speedy.
+// :[0, 2, 4, 8, 16, 32, 64, 128, 256]
+#define TX_BUFFER_SIZE 64 // MRiscoC Increase buffer for Octoprint
+
+// Host Receive Buffer Size
+// Without XON/XOFF flow control (see SERIAL_XON_XOFF below) 32 bytes should be enough.
+// To use flow control, set this buffer size to at least 1024 bytes.
+// :[0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048]
+//#define RX_BUFFER_SIZE 1024
+
+#if RX_BUFFER_SIZE >= 1024
+ // Enable to have the controller send XON/XOFF control characters to
+ // the host to signal the RX buffer is becoming full.
+ //#define SERIAL_XON_XOFF
+#endif
+
+#if ENABLED(SDSUPPORT)
+ // Enable this option to collect and display the maximum
+ // RX queue usage after transferring a file to SD.
+ //#define SERIAL_STATS_MAX_RX_QUEUED
+
+ // Enable this option to collect and display the number
+ // of dropped bytes after a file transfer to SD.
+ //#define SERIAL_STATS_DROPPED_RX
+#endif
+
+// Monitor RX buffer usage
+// Dump an error to the serial port if the serial receive buffer overflows.
+// If you see these errors, increase the RX_BUFFER_SIZE value.
+// Not supported on all platforms.
+//#define RX_BUFFER_MONITOR
+
+/**
+ * Emergency Command Parser
+ *
+ * Add a low-level parser to intercept certain commands as they
+ * enter the serial receive buffer, so they cannot be blocked.
+ * Currently handles M108, M112, M410, M876
+ * NOTE: Not yet implemented for all platforms.
+ */
+#define EMERGENCY_PARSER // MRiscoC Enabled instantaneous response to emergency commands
+
+/**
+ * Realtime Reporting (requires EMERGENCY_PARSER)
+ *
+ * - Report position and state of the machine (like Grbl).
+ * - Auto-report position during long moves.
+ * - Useful for CNC/LASER.
+ *
+ * Adds support for commands:
+ * S000 : Report State and Position while moving.
+ * P000 : Instant Pause / Hold while moving.
+ * R000 : Resume from Pause / Hold.
+ *
+ * - During Hold all Emergency Parser commands are available, as usual.
+ * - Enable NANODLP_Z_SYNC and NANODLP_ALL_AXIS for move command end-state reports.
+ */
+//#define REALTIME_REPORTING_COMMANDS
+#if ENABLED(REALTIME_REPORTING_COMMANDS)
+ //#define FULL_REPORT_TO_HOST_FEATURE // Auto-report the machine status like Grbl CNC
+#endif
+
+// Bad Serial-connections can miss a received command by sending an 'ok'
+// Therefore some clients abort after 30 seconds in a timeout.
+// Some other clients start sending commands while receiving a 'wait'.
+// This "wait" is only sent when the buffer is empty. 1 second is a good value here.
+//#define NO_TIMEOUTS 1000 // Milliseconds
+
+// Some clients will have this feature soon. This could make the NO_TIMEOUTS unnecessary.
+#define ADVANCED_OK // MRiscoC better management of buffer by host
+
+// Printrun may have trouble receiving long strings all at once.
+// This option inserts short delays between lines of serial output.
+#define SERIAL_OVERRUN_PROTECTION
+
+// For serial echo, the number of digits after the decimal point
+//#define SERIAL_FLOAT_PRECISION 4
+
+/**
+ * Set the number of proportional font spaces required to fill up a typical character space.
+ * This can help to better align the output of commands like `G29 O` Mesh Output.
+ *
+ * For clients that use a fixed-width font (like OctoPrint), leave this set to 1.0.
+ * Otherwise, adjust according to your client and font.
+ */
+#define PROPORTIONAL_FONT_RATIO 1.0
+
+// @section extras
+
+/**
+ * Extra Fan Speed
+ * Adds a secondary fan speed for each print-cooling fan.
+ * 'M106 P T3-255' : Set a secondary speed for
+ * 'M106 P T2' : Use the set secondary speed
+ * 'M106 P T1' : Restore the previous fan speed
+ */
+//#define EXTRA_FAN_SPEED
+
+/**
+ * Firmware-based and LCD-controlled retract
+ *
+ * Add G10 / G11 commands for automatic firmware-based retract / recover.
+ * Use M207 and M208 to define parameters for retract / recover.
+ *
+ * Use M209 to enable or disable auto-retract.
+ * With auto-retract enabled, all G1 E moves within the set range
+ * will be converted to firmware-based retract/recover moves.
+ *
+ * Be sure to turn off auto-retract during filament change.
+ *
+ * Note that M207 / M208 / M209 settings are saved to EEPROM.
+ */
+#define FWRETRACT // MRiscoC Enabled support for firmware based retract
+#if ENABLED(FWRETRACT)
+ //#define FWRETRACT_AUTORETRACT // Override slicer retractions // MRiscoC use slicer retract
+ #if ENABLED(FWRETRACT_AUTORETRACT)
+ #define MIN_AUTORETRACT 0.1 // (mm) Don't convert E moves under this length
+ #define MAX_AUTORETRACT 10.0 // (mm) Don't convert E moves over this length
+ #endif
+ #define RETRACT_LENGTH 5 // (mm) Default retract length (positive value) // MRiscoC Bowden
+ #define RETRACT_LENGTH_SWAP 13 // (mm) Default swap retract length (positive value)
+ #define RETRACT_FEEDRATE 40 // (mm/s) Default feedrate for retracting // MRiscoC Bowden
+ #define RETRACT_ZRAISE 0.2 // (mm) Default retract Z-raise // MRiscoC Bowden
+ #define RETRACT_RECOVER_LENGTH 0 // (mm) Default additional recover length (added to retract length on recover)
+ #define RETRACT_RECOVER_LENGTH_SWAP 0 // (mm) Default additional swap recover length (added to retract length on recover from toolchange)
+ #define RETRACT_RECOVER_FEEDRATE 40 // (mm/s) Default feedrate for recovering from retraction // MRiscoC Bowden
+ #define RETRACT_RECOVER_FEEDRATE_SWAP 8 // (mm/s) Default feedrate for recovering from swap retraction
+ #if ENABLED(MIXING_EXTRUDER)
+ //#define RETRACT_SYNC_MIXING // Retract and restore all mixing steppers simultaneously
+ #endif
+#endif
+
+/**
+ * Universal tool change settings.
+ * Applies to all types of extruders except where explicitly noted.
+ */
+#if HAS_MULTI_EXTRUDER
+ // Z raise distance for tool-change, as needed for some extruders
+ #define TOOLCHANGE_ZRAISE 2 // (mm)
+ //#define TOOLCHANGE_ZRAISE_BEFORE_RETRACT // Apply raise before swap retraction (if enabled)
+ //#define TOOLCHANGE_NO_RETURN // Never return to previous position on tool-change
+ #if ENABLED(TOOLCHANGE_NO_RETURN)
+ //#define EVENT_GCODE_AFTER_TOOLCHANGE "G12X" // Extra G-code to run after tool-change
+ #endif
+
+ /**
+ * Extra G-code to run while executing tool-change commands. Can be used to use an additional
+ * stepper motor (e.g., I axis in Configuration.h) to drive the tool-changer.
+ */
+ //#define EVENT_GCODE_TOOLCHANGE_T0 "G28 A\nG1 A0" // Extra G-code to run while executing tool-change command T0
+ //#define EVENT_GCODE_TOOLCHANGE_T1 "G1 A10" // Extra G-code to run while executing tool-change command T1
+ //#define EVENT_GCODE_TOOLCHANGE_ALWAYS_RUN // Always execute above G-code sequences. Use with caution!
+
+ /**
+ * Consider coordinates for EVENT_GCODE_TOOLCHANGE_Tx as relative to T0
+ * so that moves in the specified axes are the same for all tools.
+ */
+ //#define TC_GCODE_USE_GLOBAL_X // Use X position relative to Tool 0
+ //#define TC_GCODE_USE_GLOBAL_Y // Use Y position relative to Tool 0
+ //#define TC_GCODE_USE_GLOBAL_Z // Use Z position relative to Tool 0
+
+ /**
+ * Tool Sensors detect when tools have been picked up or dropped.
+ * Requires the pins TOOL_SENSOR1_PIN, TOOL_SENSOR2_PIN, etc.
+ */
+ //#define TOOL_SENSOR
+
+ /**
+ * Retract and prime filament on tool-change to reduce
+ * ooze and stringing and to get cleaner transitions.
+ */
+ //#define TOOLCHANGE_FILAMENT_SWAP
+ #if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
+ // Load / Unload
+ #define TOOLCHANGE_FS_LENGTH 12 // (mm) Load / Unload length
+ #define TOOLCHANGE_FS_EXTRA_RESUME_LENGTH 0 // (mm) Extra length for better restart. Adjust with LCD or M217 B.
+ #define TOOLCHANGE_FS_RETRACT_SPEED (50*60) // (mm/min) (Unloading)
+ #define TOOLCHANGE_FS_UNRETRACT_SPEED (25*60) // (mm/min) (On SINGLENOZZLE or Bowden loading must be slowed down)
+
+ // Longer prime to clean out a SINGLENOZZLE
+ #define TOOLCHANGE_FS_EXTRA_PRIME 0 // (mm) Extra priming length
+ #define TOOLCHANGE_FS_PRIME_SPEED (4.6*60) // (mm/min) Extra priming feedrate
+ #define TOOLCHANGE_FS_WIPE_RETRACT 0 // (mm) Cutting retraction out of park, for less stringing, better wipe, etc. Adjust with LCD or M217 G.
+
+ // Cool after prime to reduce stringing
+ #define TOOLCHANGE_FS_FAN -1 // Fan index or -1 to skip
+ #define TOOLCHANGE_FS_FAN_SPEED 255 // 0-255
+ #define TOOLCHANGE_FS_FAN_TIME 10 // (seconds)
+
+ // Use TOOLCHANGE_FS_PRIME_SPEED feedrate the first time each extruder is primed
+ //#define TOOLCHANGE_FS_SLOW_FIRST_PRIME
+
+ /**
+ * Prime T0 the first time T0 is sent to the printer:
+ * [ Power-On -> T0 { Activate & Prime T0 } -> T1 { Retract T0, Activate & Prime T1 } ]
+ * If disabled, no priming on T0 until switching back to T0 from another extruder:
+ * [ Power-On -> T0 { T0 Activated } -> T1 { Activate & Prime T1 } -> T0 { Retract T1, Activate & Prime T0 } ]
+ * Enable with M217 V1 before printing to avoid unwanted priming on host connect.
+ */
+ //#define TOOLCHANGE_FS_PRIME_FIRST_USED
+
+ /**
+ * Tool Change Migration
+ * This feature provides G-code and LCD options to switch tools mid-print.
+ * All applicable tool properties are migrated so the print can continue.
+ * Tools must be closely matching and other restrictions may apply.
+ * Useful to:
+ * - Change filament color without interruption
+ * - Switch spools automatically on filament runout
+ * - Switch to a different nozzle on an extruder jam
+ */
+ #define TOOLCHANGE_MIGRATION_FEATURE
+
+ #endif
+
+ /**
+ * Position to park head during tool change.
+ * Doesn't apply to SWITCHING_TOOLHEAD, DUAL_X_CARRIAGE, or PARKING_EXTRUDER
+ */
+ //#define TOOLCHANGE_PARK
+ #if ENABLED(TOOLCHANGE_PARK)
+ #define TOOLCHANGE_PARK_XY { X_MIN_POS + 10, Y_MIN_POS + 10 }
+ #define TOOLCHANGE_PARK_XY_FEEDRATE 6000 // (mm/min)
+ //#define TOOLCHANGE_PARK_X_ONLY // X axis only move
+ //#define TOOLCHANGE_PARK_Y_ONLY // Y axis only move
+ #endif
+#endif // HAS_MULTI_EXTRUDER
+
+// @section advanced pause
+
+/**
+ * Advanced Pause for Filament Change
+ * - Adds the G-code M600 Filament Change to initiate a filament change.
+ * - This feature is required for the default FILAMENT_RUNOUT_SCRIPT.
+ *
+ * Requirements:
+ * - For Filament Change parking enable and configure NOZZLE_PARK_FEATURE.
+ * - For user interaction enable an LCD display, HOST_PROMPT_SUPPORT, or EMERGENCY_PARSER.
+ *
+ * Enable PARK_HEAD_ON_PAUSE to add the G-code M125 Pause and Park.
+ */
+#define ADVANCED_PAUSE_FEATURE // MRiscoC Enabled
+#if ENABLED(ADVANCED_PAUSE_FEATURE)
+ #define PAUSE_PARK_RETRACT_FEEDRATE 60 // (mm/s) Initial retract feedrate.
+ #define PAUSE_PARK_RETRACT_LENGTH 2 // (mm) Initial retract.
+ // This short retract is done immediately, before parking the nozzle.
+ #define FILAMENT_CHANGE_UNLOAD_FEEDRATE 20 // (mm/s) Unload filament feedrate. This can be pretty fast. // MRiscoC Increased filament unload speed
+ #define FILAMENT_CHANGE_UNLOAD_ACCEL 25 // (mm/s^2) Lower acceleration may allow a faster feedrate.
+ #define FILAMENT_CHANGE_UNLOAD_LENGTH 100 // (mm) The length of filament for a complete unload.
+ // For Bowden, the full length of the tube and nozzle.
+ // For direct drive, the full length of the nozzle.
+ // Set to 0 for manual unloading.
+ #define FILAMENT_CHANGE_SLOW_LOAD_FEEDRATE 6 // (mm/s) Slow move when starting load.
+ #define FILAMENT_CHANGE_SLOW_LOAD_LENGTH 0 // (mm) Slow length, to allow time to insert material.
+ // 0 to disable start loading and skip to fast load only
+ #define FILAMENT_CHANGE_FAST_LOAD_FEEDRATE 12 // (mm/s) Load filament feedrate. This can be pretty fast. // MRiscoC Increased filament load speed
+ #define FILAMENT_CHANGE_FAST_LOAD_ACCEL 25 // (mm/s^2) Lower acceleration may allow a faster feedrate.
+ #define FILAMENT_CHANGE_FAST_LOAD_LENGTH 0 // (mm) Load length of filament, from extruder gear to nozzle.
+ // For Bowden, the full length of the tube and nozzle.
+ // For direct drive, the full length of the nozzle.
+ //#define ADVANCED_PAUSE_CONTINUOUS_PURGE // Purge continuously up to the purge length until interrupted.
+ #define ADVANCED_PAUSE_PURGE_FEEDRATE 3 // (mm/s) Extrude feedrate (after loading). Should be slower than load feedrate.
+ #define ADVANCED_PAUSE_PURGE_LENGTH 50 // (mm) Length to extrude after loading.
+ // Set to 0 for manual extrusion.
+ // Filament can be extruded repeatedly from the Filament Change menu
+ // until extrusion is consistent, and to purge old filament.
+ #define ADVANCED_PAUSE_RESUME_PRIME 0 // (mm) Extra distance to prime nozzle after returning from park.
+ //#define ADVANCED_PAUSE_FANS_PAUSE // Turn off print-cooling fans while the machine is paused.
+
+ // Filament Unload does a Retract, Delay, and Purge first:
+ #define FILAMENT_UNLOAD_PURGE_RETRACT 13 // (mm) Unload initial retract length.
+ #define FILAMENT_UNLOAD_PURGE_DELAY 5000 // (ms) Delay for the filament to cool after retract.
+ #define FILAMENT_UNLOAD_PURGE_LENGTH 8 // (mm) An unretract is done, then this length is purged.
+ #define FILAMENT_UNLOAD_PURGE_FEEDRATE 25 // (mm/s) feedrate to purge before unload
+
+ #define PAUSE_PARK_NOZZLE_TIMEOUT 90 // (seconds) Time limit before the nozzle is turned off for safety.
+ #define FILAMENT_CHANGE_ALERT_BEEPS 10 // Number of alert beeps to play when a response is needed.
+ #define PAUSE_PARK_NO_STEPPER_TIMEOUT // Enable for XYZ steppers to stay powered on during filament change.
+ //#define FILAMENT_CHANGE_RESUME_ON_INSERT // Automatically continue / load filament when runout sensor is triggered again.
+ //#define PAUSE_REHEAT_FAST_RESUME // Reduce number of waits by not prompting again post-timeout before continuing.
+
+ #define PARK_HEAD_ON_PAUSE // Park the nozzle during pause and filament change. // MRiscoC Enabled park head when pause command was issued
+ //#define HOME_BEFORE_FILAMENT_CHANGE // If needed, home before parking for filament change
+
+ #define FILAMENT_LOAD_UNLOAD_GCODES // Add M701/M702 Load/Unload G-codes, plus Load/Unload in the LCD Prepare menu. // MRiscoC Enabled load/unload Filament G-codes
+ //#define FILAMENT_UNLOAD_ALL_EXTRUDERS // Allow M702 to unload all extruders above a minimum target temp (as set by M302)
+#endif
+
+// @section tmc_smart
+
+/**
+ * Trinamic Smart Drivers
+ *
+ * To use TMC2130, TMC2160, TMC2660, TMC5130, TMC5160 stepper drivers in SPI mode:
+ * - Connect your SPI pins to the Hardware SPI interface on the board.
+ * Some boards have simple jumper connections! See your board's documentation.
+ * - Define the required Stepper CS pins in your `pins_MYBOARD.h` file.
+ * (See the RAMPS pins, for example.)
+ * - You can also use Software SPI with GPIO pins instead of Hardware SPI.
+ *
+ * To use TMC220x stepper drivers with Serial UART:
+ * - Connect PDN_UART to the #_SERIAL_TX_PIN through a 1K resistor.
+ * For reading capabilities also connect PDN_UART to #_SERIAL_RX_PIN with no resistor.
+ * Some boards have simple jumper connections! See your board's documentation.
+ * - These drivers can also be used with Hardware Serial.
+ *
+ * The TMC26XStepper library is required for TMC26X stepper drivers.
+ * https://github.com/MarlinFirmware/TMC26XStepper
+ *
+ * The TMCStepper library is required for other TMC stepper drivers.
+ * https://github.com/teemuatlut/TMCStepper
+ *
+ * @section tmc/config
+ */
+#if HAS_TRINAMIC_CONFIG || HAS_TMC26X
+
+ #define HOLD_MULTIPLIER 0.5 // Scales down the holding current from run current
+
+ /**
+ * Interpolate microsteps to 256
+ * Override for each driver with _INTERPOLATE settings below
+ */
+ #define INTERPOLATE true
+
+ #if AXIS_IS_TMC_CONFIG(X)
+ #define X_CURRENT 800 // (mA) RMS current. Multiply by 1.414 for peak current.
+ #define X_CURRENT_HOME X_CURRENT // (mA) RMS current for sensorless homing
+ #define X_MICROSTEPS 16 // 0..256
+ #define X_RSENSE 0.11 // Multiplied x1000 for TMC26X
+ #define X_CHAIN_POS -1 // -1..0: Not chained. 1: MCU MOSI connected. 2: Next in chain, ...
+ //#define X_INTERPOLATE true // Enable to override 'INTERPOLATE' for the X axis
+ //#define X_HOLD_MULTIPLIER 0.5 // Enable to override 'HOLD_MULTIPLIER' for the X axis
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(X2)
+ #define X2_CURRENT 800
+ #define X2_CURRENT_HOME X2_CURRENT
+ #define X2_MICROSTEPS X_MICROSTEPS
+ #define X2_RSENSE 0.11
+ #define X2_CHAIN_POS -1
+ //#define X2_INTERPOLATE true
+ //#define X2_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(Y)
+ #define Y_CURRENT 800
+ #define Y_CURRENT_HOME Y_CURRENT
+ #define Y_MICROSTEPS 16
+ #define Y_RSENSE 0.11
+ #define Y_CHAIN_POS -1
+ //#define Y_INTERPOLATE true
+ //#define Y_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(Y2)
+ #define Y2_CURRENT 800
+ #define Y2_CURRENT_HOME Y2_CURRENT
+ #define Y2_MICROSTEPS Y_MICROSTEPS
+ #define Y2_RSENSE 0.11
+ #define Y2_CHAIN_POS -1
+ //#define Y2_INTERPOLATE true
+ //#define Y2_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(Z)
+ #define Z_CURRENT 800
+ #define Z_CURRENT_HOME Z_CURRENT
+ #define Z_MICROSTEPS 16
+ #define Z_RSENSE 0.11
+ #define Z_CHAIN_POS -1
+ //#define Z_INTERPOLATE true
+ //#define Z_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(Z2)
+ #define Z2_CURRENT 800
+ #define Z2_CURRENT_HOME Z2_CURRENT
+ #define Z2_MICROSTEPS Z_MICROSTEPS
+ #define Z2_RSENSE 0.11
+ #define Z2_CHAIN_POS -1
+ //#define Z2_INTERPOLATE true
+ //#define Z2_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(Z3)
+ #define Z3_CURRENT 800
+ #define Z3_CURRENT_HOME Z3_CURRENT
+ #define Z3_MICROSTEPS Z_MICROSTEPS
+ #define Z3_RSENSE 0.11
+ #define Z3_CHAIN_POS -1
+ //#define Z3_INTERPOLATE true
+ //#define Z3_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(Z4)
+ #define Z4_CURRENT 800
+ #define Z4_CURRENT_HOME Z4_CURRENT
+ #define Z4_MICROSTEPS Z_MICROSTEPS
+ #define Z4_RSENSE 0.11
+ #define Z4_CHAIN_POS -1
+ //#define Z4_INTERPOLATE true
+ //#define Z4_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(I)
+ #define I_CURRENT 800
+ #define I_CURRENT_HOME I_CURRENT
+ #define I_MICROSTEPS 16
+ #define I_RSENSE 0.11
+ #define I_CHAIN_POS -1
+ //#define I_INTERPOLATE true
+ //#define I_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(J)
+ #define J_CURRENT 800
+ #define J_CURRENT_HOME J_CURRENT
+ #define J_MICROSTEPS 16
+ #define J_RSENSE 0.11
+ #define J_CHAIN_POS -1
+ //#define J_INTERPOLATE true
+ //#define J_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(K)
+ #define K_CURRENT 800
+ #define K_CURRENT_HOME K_CURRENT
+ #define K_MICROSTEPS 16
+ #define K_RSENSE 0.11
+ #define K_CHAIN_POS -1
+ //#define K_INTERPOLATE true
+ //#define K_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(U)
+ #define U_CURRENT 800
+ #define U_CURRENT_HOME U_CURRENT
+ #define U_MICROSTEPS 8
+ #define U_RSENSE 0.11
+ #define U_CHAIN_POS -1
+ //#define U_INTERPOLATE true
+ //#define U_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(V)
+ #define V_CURRENT 800
+ #define V_CURRENT_HOME V_CURRENT
+ #define V_MICROSTEPS 8
+ #define V_RSENSE 0.11
+ #define V_CHAIN_POS -1
+ //#define V_INTERPOLATE true
+ //#define V_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(W)
+ #define W_CURRENT 800
+ #define W_CURRENT_HOME W_CURRENT
+ #define W_MICROSTEPS 8
+ #define W_RSENSE 0.11
+ #define W_CHAIN_POS -1
+ //#define W_INTERPOLATE true
+ //#define W_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(E0)
+ #define E0_CURRENT 800
+ #define E0_MICROSTEPS 16
+ #define E0_RSENSE 0.11
+ #define E0_CHAIN_POS -1
+ //#define E0_INTERPOLATE true
+ //#define E0_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(E1)
+ #define E1_CURRENT 800
+ #define E1_MICROSTEPS E0_MICROSTEPS
+ #define E1_RSENSE 0.11
+ #define E1_CHAIN_POS -1
+ //#define E1_INTERPOLATE true
+ //#define E1_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(E2)
+ #define E2_CURRENT 800
+ #define E2_MICROSTEPS E0_MICROSTEPS
+ #define E2_RSENSE 0.11
+ #define E2_CHAIN_POS -1
+ //#define E2_INTERPOLATE true
+ //#define E2_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(E3)
+ #define E3_CURRENT 800
+ #define E3_MICROSTEPS E0_MICROSTEPS
+ #define E3_RSENSE 0.11
+ #define E3_CHAIN_POS -1
+ //#define E3_INTERPOLATE true
+ //#define E3_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(E4)
+ #define E4_CURRENT 800
+ #define E4_MICROSTEPS E0_MICROSTEPS
+ #define E4_RSENSE 0.11
+ #define E4_CHAIN_POS -1
+ //#define E4_INTERPOLATE true
+ //#define E4_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(E5)
+ #define E5_CURRENT 800
+ #define E5_MICROSTEPS E0_MICROSTEPS
+ #define E5_RSENSE 0.11
+ #define E5_CHAIN_POS -1
+ //#define E5_INTERPOLATE true
+ //#define E5_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(E6)
+ #define E6_CURRENT 800
+ #define E6_MICROSTEPS E0_MICROSTEPS
+ #define E6_RSENSE 0.11
+ #define E6_CHAIN_POS -1
+ //#define E6_INTERPOLATE true
+ //#define E6_HOLD_MULTIPLIER 0.5
+ #endif
+
+ #if AXIS_IS_TMC_CONFIG(E7)
+ #define E7_CURRENT 800
+ #define E7_MICROSTEPS E0_MICROSTEPS
+ #define E7_RSENSE 0.11
+ #define E7_CHAIN_POS -1
+ //#define E7_INTERPOLATE true
+ //#define E7_HOLD_MULTIPLIER 0.5
+ #endif
+
+ // @section tmc/spi
+
+ /**
+ * Override default SPI pins for TMC2130, TMC2160, TMC2660, TMC5130 and TMC5160 drivers here.
+ * The default pins can be found in your board's pins file.
+ */
+ //#define X_CS_PIN -1
+ //#define Y_CS_PIN -1
+ //#define Z_CS_PIN -1
+ //#define X2_CS_PIN -1
+ //#define Y2_CS_PIN -1
+ //#define Z2_CS_PIN -1
+ //#define Z3_CS_PIN -1
+ //#define Z4_CS_PIN -1
+ //#define I_CS_PIN -1
+ //#define J_CS_PIN -1
+ //#define K_CS_PIN -1
+ //#define U_CS_PIN -1
+ //#define V_CS_PIN -1
+ //#define W_CS_PIN -1
+ //#define E0_CS_PIN -1
+ //#define E1_CS_PIN -1
+ //#define E2_CS_PIN -1
+ //#define E3_CS_PIN -1
+ //#define E4_CS_PIN -1
+ //#define E5_CS_PIN -1
+ //#define E6_CS_PIN -1
+ //#define E7_CS_PIN -1
+
+ /**
+ * Software option for SPI driven drivers (TMC2130, TMC2160, TMC2660, TMC5130 and TMC5160).
+ * The default SW SPI pins are defined the respective pins files,
+ * but you can override or define them here.
+ */
+ //#define TMC_USE_SW_SPI
+ //#define TMC_SW_MOSI -1
+ //#define TMC_SW_MISO -1
+ //#define TMC_SW_SCK -1
+
+ // @section tmc/serial
+
+ /**
+ * Four TMC2209 drivers can use the same HW/SW serial port with hardware configured addresses.
+ * Set the address using jumpers on pins MS1 and MS2.
+ * Address | MS1 | MS2
+ * 0 | LOW | LOW
+ * 1 | HIGH | LOW
+ * 2 | LOW | HIGH
+ * 3 | HIGH | HIGH
+ *
+ * Set *_SERIAL_TX_PIN and *_SERIAL_RX_PIN to match for all drivers
+ * on the same serial port, either here or in your board's pins file.
+ */
+ //#define X_SLAVE_ADDRESS 0
+ //#define Y_SLAVE_ADDRESS 0
+ //#define Z_SLAVE_ADDRESS 0
+ //#define X2_SLAVE_ADDRESS 0
+ //#define Y2_SLAVE_ADDRESS 0
+ //#define Z2_SLAVE_ADDRESS 0
+ //#define Z3_SLAVE_ADDRESS 0
+ //#define Z4_SLAVE_ADDRESS 0
+ //#define I_SLAVE_ADDRESS 0
+ //#define J_SLAVE_ADDRESS 0
+ //#define K_SLAVE_ADDRESS 0
+ //#define U_SLAVE_ADDRESS 0
+ //#define V_SLAVE_ADDRESS 0
+ //#define W_SLAVE_ADDRESS 0
+ //#define E0_SLAVE_ADDRESS 0
+ //#define E1_SLAVE_ADDRESS 0
+ //#define E2_SLAVE_ADDRESS 0
+ //#define E3_SLAVE_ADDRESS 0
+ //#define E4_SLAVE_ADDRESS 0
+ //#define E5_SLAVE_ADDRESS 0
+ //#define E6_SLAVE_ADDRESS 0
+ //#define E7_SLAVE_ADDRESS 0
+
+ // @section tmc/smart
+
+ /**
+ * Software enable
+ *
+ * Use for drivers that do not use a dedicated enable pin, but rather handle the same
+ * function through a communication line such as SPI or UART.
+ */
+ //#define SOFTWARE_DRIVER_ENABLE
+
+ // @section tmc/stealthchop
+
+ /**
+ * TMC2130, TMC2160, TMC2208, TMC2209, TMC5130 and TMC5160 only
+ * Use Trinamic's ultra quiet stepping mode.
+ * When disabled, Marlin will use spreadCycle stepping mode.
+ */
+ #if HAS_STEALTHCHOP
+ #define STEALTHCHOP_XY
+ #define STEALTHCHOP_Z
+ #define STEALTHCHOP_I
+ #define STEALTHCHOP_J
+ #define STEALTHCHOP_K
+ #define STEALTHCHOP_U
+ #define STEALTHCHOP_V
+ #define STEALTHCHOP_W
+ #define STEALTHCHOP_E
+ #endif
+
+ /**
+ * Optimize spreadCycle chopper parameters by using predefined parameter sets
+ * or with the help of an example included in the library.
+ * Provided parameter sets are
+ * CHOPPER_DEFAULT_12V
+ * CHOPPER_DEFAULT_19V
+ * CHOPPER_DEFAULT_24V
+ * CHOPPER_DEFAULT_36V
+ * CHOPPER_09STEP_24V // 0.9 degree steppers (24V)
+ * CHOPPER_PRUSAMK3_24V // Imported parameters from the official Průša firmware for MK3 (24V)
+ * CHOPPER_MARLIN_119 // Old defaults from Marlin v1.1.9
+ *
+ * Define your own with:
+ * { , , hysteresis_start[1..8] }
+ */
+ #define CHOPPER_TIMING CHOPPER_DEFAULT_24V // All axes (override below) // MRiscoC Correct value for UART mode drivers
+ //#define CHOPPER_TIMING_X CHOPPER_TIMING // For X Axes (override below)
+ //#define CHOPPER_TIMING_X2 CHOPPER_TIMING_X
+ //#define CHOPPER_TIMING_Y CHOPPER_TIMING // For Y Axes (override below)
+ //#define CHOPPER_TIMING_Y2 CHOPPER_TIMING_Y
+ //#define CHOPPER_TIMING_Z CHOPPER_TIMING // For Z Axes (override below)
+ //#define CHOPPER_TIMING_Z2 CHOPPER_TIMING_Z
+ //#define CHOPPER_TIMING_Z3 CHOPPER_TIMING_Z
+ //#define CHOPPER_TIMING_Z4 CHOPPER_TIMING_Z
+ //#define CHOPPER_TIMING_I CHOPPER_TIMING // For I Axis
+ //#define CHOPPER_TIMING_J CHOPPER_TIMING // For J Axis
+ //#define CHOPPER_TIMING_K CHOPPER_TIMING // For K Axis
+ //#define CHOPPER_TIMING_U CHOPPER_TIMING // For U Axis
+ //#define CHOPPER_TIMING_V CHOPPER_TIMING // For V Axis
+ //#define CHOPPER_TIMING_W CHOPPER_TIMING // For W Axis
+ //#define CHOPPER_TIMING_E CHOPPER_TIMING // For Extruders (override below)
+ //#define CHOPPER_TIMING_E1 CHOPPER_TIMING_E
+ //#define CHOPPER_TIMING_E2 CHOPPER_TIMING_E
+ //#define CHOPPER_TIMING_E3 CHOPPER_TIMING_E
+ //#define CHOPPER_TIMING_E4 CHOPPER_TIMING_E
+ //#define CHOPPER_TIMING_E5 CHOPPER_TIMING_E
+ //#define CHOPPER_TIMING_E6 CHOPPER_TIMING_E
+ //#define CHOPPER_TIMING_E7 CHOPPER_TIMING_E
+
+ // @section tmc/status
+
+ /**
+ * Monitor Trinamic drivers
+ * for error conditions like overtemperature and short to ground.
+ * To manage over-temp Marlin can decrease the driver current until the error condition clears.
+ * Other detected conditions can be used to stop the current print.
+ * Relevant G-codes:
+ * M906 - Set or get motor current in milliamps using axis codes X, Y, Z, E. Report values if no axis codes given.
+ * M911 - Report stepper driver overtemperature pre-warn condition.
+ * M912 - Clear stepper driver overtemperature pre-warn condition flag.
+ * M122 - Report driver parameters (Requires TMC_DEBUG)
+ */
+ //#define MONITOR_DRIVER_STATUS
+
+ #if ENABLED(MONITOR_DRIVER_STATUS)
+ #define CURRENT_STEP_DOWN 50 // [mA]
+ #define REPORT_CURRENT_CHANGE
+ #define STOP_ON_ERROR
+ #endif
+
+ // @section tmc/hybrid
+
+ /**
+ * TMC2130, TMC2160, TMC2208, TMC2209, TMC5130 and TMC5160 only
+ * The driver will switch to spreadCycle when stepper speed is over HYBRID_THRESHOLD.
+ * This mode allows for faster movements at the expense of higher noise levels.
+ * STEALTHCHOP_(XY|Z|E) must be enabled to use HYBRID_THRESHOLD.
+ * M913 X/Y/Z/E to live tune the setting
+ */
+ //#define HYBRID_THRESHOLD
+
+ #define X_HYBRID_THRESHOLD 100 // [mm/s]
+ #define X2_HYBRID_THRESHOLD 100
+ #define Y_HYBRID_THRESHOLD 100
+ #define Y2_HYBRID_THRESHOLD 100
+ #define Z_HYBRID_THRESHOLD 3
+ #define Z2_HYBRID_THRESHOLD 3
+ #define Z3_HYBRID_THRESHOLD 3
+ #define Z4_HYBRID_THRESHOLD 3
+ #define I_HYBRID_THRESHOLD 3 // [linear=mm/s, rotational=°/s]
+ #define J_HYBRID_THRESHOLD 3 // [linear=mm/s, rotational=°/s]
+ #define K_HYBRID_THRESHOLD 3 // [linear=mm/s, rotational=°/s]
+ #define U_HYBRID_THRESHOLD 3 // [mm/s]
+ #define V_HYBRID_THRESHOLD 3
+ #define W_HYBRID_THRESHOLD 3
+ #define E0_HYBRID_THRESHOLD 30
+ #define E1_HYBRID_THRESHOLD 30
+ #define E2_HYBRID_THRESHOLD 30
+ #define E3_HYBRID_THRESHOLD 30
+ #define E4_HYBRID_THRESHOLD 30
+ #define E5_HYBRID_THRESHOLD 30
+ #define E6_HYBRID_THRESHOLD 30
+ #define E7_HYBRID_THRESHOLD 30
+
+ /**
+ * Use StallGuard to home / probe X, Y, Z.
+ *
+ * TMC2130, TMC2160, TMC2209, TMC2660, TMC5130, and TMC5160 only
+ * Connect the stepper driver's DIAG1 pin to the X/Y endstop pin.
+ * X, Y, and Z homing will always be done in spreadCycle mode.
+ *
+ * X/Y/Z_STALL_SENSITIVITY is the default stall threshold.
+ * Use M914 X Y Z to set the stall threshold at runtime:
+ *
+ * Sensitivity TMC2209 Others
+ * HIGHEST 255 -64 (Too sensitive => False positive)
+ * LOWEST 0 63 (Too insensitive => No trigger)
+ *
+ * It is recommended to set HOMING_BUMP_MM to { 0, 0, 0 }.
+ *
+ * SPI_ENDSTOPS *** Beta feature! *** TMC2130/TMC5160 Only ***
+ * Poll the driver through SPI to determine load when homing.
+ * Removes the need for a wire from DIAG1 to an endstop pin.
+ *
+ * IMPROVE_HOMING_RELIABILITY tunes acceleration and jerk when
+ * homing and adds a guard period for endstop triggering.
+ *
+ * Comment *_STALL_SENSITIVITY to disable sensorless homing for that axis.
+ * @section tmc/stallguard
+ */
+ //#define SENSORLESS_HOMING // StallGuard capable drivers only
+
+ #if EITHER(SENSORLESS_HOMING, SENSORLESS_PROBING)
+ // TMC2209: 0...255. TMC2130: -64...63
+ #define X_STALL_SENSITIVITY 8
+ #define X2_STALL_SENSITIVITY X_STALL_SENSITIVITY
+ #define Y_STALL_SENSITIVITY 8
+ #define Y2_STALL_SENSITIVITY Y_STALL_SENSITIVITY
+ //#define Z_STALL_SENSITIVITY 8
+ //#define Z2_STALL_SENSITIVITY Z_STALL_SENSITIVITY
+ //#define Z3_STALL_SENSITIVITY Z_STALL_SENSITIVITY
+ //#define Z4_STALL_SENSITIVITY Z_STALL_SENSITIVITY
+ //#define I_STALL_SENSITIVITY 8
+ //#define J_STALL_SENSITIVITY 8
+ //#define K_STALL_SENSITIVITY 8
+ //#define U_STALL_SENSITIVITY 8
+ //#define V_STALL_SENSITIVITY 8
+ //#define W_STALL_SENSITIVITY 8
+ //#define SPI_ENDSTOPS // TMC2130 only
+ //#define IMPROVE_HOMING_RELIABILITY
+ #endif
+
+ // @section tmc/config
+
+ /**
+ * TMC Homing stepper phase.
+ *
+ * Improve homing repeatability by homing to stepper coil's nearest absolute
+ * phase position. Trinamic drivers use a stepper phase table with 1024 values
+ * spanning 4 full steps with 256 positions each (ergo, 1024 positions).
+ * Full step positions (128, 384, 640, 896) have the highest holding torque.
+ *
+ * Values from 0..1023, -1 to disable homing phase for that axis.
+ */
+ //#define TMC_HOME_PHASE { 896, 896, 896 }
+
+ /**
+ * Beta feature!
+ * Create a 50/50 square wave step pulse optimal for stepper drivers.
+ */
+ #define SQUARE_WAVE_STEPPING // Ender Configs
+
+ /**
+ * Enable M122 debugging command for TMC stepper drivers.
+ * M122 S0/1 will enable continuous reporting.
+ */
+ //#define TMC_DEBUG
+
+ /**
+ * You can set your own advanced settings by filling in predefined functions.
+ * A list of available functions can be found on the library github page
+ * https://github.com/teemuatlut/TMCStepper
+ *
+ * Example:
+ * #define TMC_ADV() { \
+ * stepperX.diag0_otpw(1); \
+ * stepperY.intpol(0); \
+ * }
+ */
+ #define TMC_ADV() { }
+
+#endif // HAS_TRINAMIC_CONFIG || HAS_TMC26X
+
+// @section i2cbus
+
+//
+// I2C Master ID for LPC176x LCD and Digital Current control
+// Does not apply to other peripherals based on the Wire library.
+//
+//#define I2C_MASTER_ID 1 // Set a value from 0 to 2
+
+/**
+ * TWI/I2C BUS
+ *
+ * This feature is an EXPERIMENTAL feature so it shall not be used on production
+ * machines. Enabling this will allow you to send and receive I2C data from slave
+ * devices on the bus.
+ *
+ * ; Example #1
+ * ; This macro send the string "Marlin" to the slave device with address 0x63 (99)
+ * ; It uses multiple M260 commands with one B arg
+ * M260 A99 ; Target slave address
+ * M260 B77 ; M
+ * M260 B97 ; a
+ * M260 B114 ; r
+ * M260 B108 ; l
+ * M260 B105 ; i
+ * M260 B110 ; n
+ * M260 S1 ; Send the current buffer
+ *
+ * ; Example #2
+ * ; Request 6 bytes from slave device with address 0x63 (99)
+ * M261 A99 B5
+ *
+ * ; Example #3
+ * ; Example serial output of a M261 request
+ * echo:i2c-reply: from:99 bytes:5 data:hello
+ */
+
+//#define EXPERIMENTAL_I2CBUS
+#if ENABLED(EXPERIMENTAL_I2CBUS)
+ #define I2C_SLAVE_ADDRESS 0 // Set a value from 8 to 127 to act as a slave
+#endif
+
+// @section photo
+
+/**
+ * Photo G-code
+ * Add the M240 G-code to take a photo.
+ * The photo can be triggered by a digital pin or a physical movement.
+ */
+//#define PHOTO_GCODE
+#if ENABLED(PHOTO_GCODE)
+ // A position to move to (and raise Z) before taking the photo
+ //#define PHOTO_POSITION { X_MAX_POS - 5, Y_MAX_POS, 0 } // { xpos, ypos, zraise } (M240 X Y Z)
+ //#define PHOTO_DELAY_MS 100 // (ms) Duration to pause before moving back (M240 P)
+ //#define PHOTO_RETRACT_MM 6.5 // (mm) E retract/recover for the photo move (M240 R S)
+
+ // Canon RC-1 or homebrew digital camera trigger
+ // Data from: https://www.doc-diy.net/photo/rc-1_hacked/
+ //#define PHOTOGRAPH_PIN 23
+
+ // Canon Hack Development Kit
+ // https://captain-slow.dk/2014/03/09/3d-printing-timelapses/
+ //#define CHDK_PIN 4
+
+ // Optional second move with delay to trigger the camera shutter
+ //#define PHOTO_SWITCH_POSITION { X_MAX_POS, Y_MAX_POS } // { xpos, ypos } (M240 I J)
+
+ // Duration to hold the switch or keep CHDK_PIN high
+ //#define PHOTO_SWITCH_MS 50 // (ms) (M240 D)
+
+ /**
+ * PHOTO_PULSES_US may need adjustment depending on board and camera model.
+ * Pin must be running at 48.4kHz.
+ * Be sure to use a PHOTOGRAPH_PIN which can rise and fall quick enough.
+ * (e.g., MKS SBase temp sensor pin was too slow, so used P1.23 on J8.)
+ *
+ * Example pulse data for Nikon: https://bit.ly/2FKD0Aq
+ * IR Wiring: https://git.io/JvJf7
+ */
+ //#define PHOTO_PULSES_US { 2000, 27850, 400, 1580, 400, 3580, 400 } // (µs) Durations for each 48.4kHz oscillation
+ #ifdef PHOTO_PULSES_US
+ #define PHOTO_PULSE_DELAY_US 13 // (µs) Approximate duration of each HIGH and LOW pulse in the oscillation
+ #endif
+#endif
+
+// @section cnc
+
+/**
+ * Spindle & Laser control
+ *
+ * Add the M3, M4, and M5 commands to turn the spindle/laser on and off, and
+ * to set spindle speed, spindle direction, and laser power.
+ *
+ * SuperPid is a router/spindle speed controller used in the CNC milling community.
+ * Marlin can be used to turn the spindle on and off. It can also be used to set
+ * the spindle speed from 5,000 to 30,000 RPM.
+ *
+ * You'll need to select a pin for the ON/OFF function and optionally choose a 0-5V
+ * hardware PWM pin for the speed control and a pin for the rotation direction.
+ *
+ * See https://marlinfw.org/docs/configuration/2.0.9/laser_spindle.html for more config details.
+ */
+//#define SPINDLE_FEATURE
+//#define LASER_FEATURE
+#if EITHER(SPINDLE_FEATURE, LASER_FEATURE)
+ #define SPINDLE_LASER_ACTIVE_STATE LOW // Set to "HIGH" if SPINDLE_LASER_ENA_PIN is active HIGH
+
+ #define SPINDLE_LASER_USE_PWM // Enable if your controller supports setting the speed/power
+ #if ENABLED(SPINDLE_LASER_USE_PWM)
+ #define SPINDLE_LASER_PWM_INVERT false // Set to "true" if the speed/power goes up when you want it to go slower
+ #define SPINDLE_LASER_FREQUENCY 2500 // (Hz) Spindle/laser frequency (only on supported HALs: AVR, ESP32, and LPC)
+ // ESP32: If SPINDLE_LASER_PWM_PIN is onboard then <=78125Hz. For I2S expander
+ // the frequency determines the PWM resolution. 2500Hz = 0-100, 977Hz = 0-255, ...
+ // (250000 / SPINDLE_LASER_FREQUENCY) = max value.
+ #endif
+
+ //#define AIR_EVACUATION // Cutter Vacuum / Laser Blower motor control with G-codes M10-M11
+ #if ENABLED(AIR_EVACUATION)
+ #define AIR_EVACUATION_ACTIVE LOW // Set to "HIGH" if the on/off function is active HIGH
+ //#define AIR_EVACUATION_PIN 42 // Override the default Cutter Vacuum or Laser Blower pin
+ #endif
+
+ //#define AIR_ASSIST // Air Assist control with G-codes M8-M9
+ #if ENABLED(AIR_ASSIST)
+ #define AIR_ASSIST_ACTIVE LOW // Active state on air assist pin
+ //#define AIR_ASSIST_PIN 44 // Override the default Air Assist pin
+ #endif
+
+ //#define SPINDLE_SERVO // A servo converting an angle to spindle power
+ #ifdef SPINDLE_SERVO
+ #define SPINDLE_SERVO_NR 0 // Index of servo used for spindle control
+ #define SPINDLE_SERVO_MIN 10 // Minimum angle for servo spindle
+ #endif
+
+ /**
+ * Speed / Power can be set ('M3 S') and displayed in terms of:
+ * - PWM255 (S0 - S255)
+ * - PERCENT (S0 - S100)
+ * - RPM (S0 - S50000) Best for use with a spindle
+ * - SERVO (S0 - S180)
+ */
+ #define CUTTER_POWER_UNIT PWM255
+
+ /**
+ * Relative Cutter Power
+ * Normally, 'M3 O' sets
+ * OCR power is relative to the range SPEED_POWER_MIN...SPEED_POWER_MAX.
+ * so input powers of 0...255 correspond to SPEED_POWER_MIN...SPEED_POWER_MAX
+ * instead of normal range (0 to SPEED_POWER_MAX).
+ * Best used with (e.g.) SuperPID router controller: S0 = 5,000 RPM and S255 = 30,000 RPM
+ */
+ //#define CUTTER_POWER_RELATIVE // Set speed proportional to [SPEED_POWER_MIN...SPEED_POWER_MAX]
+
+ #if ENABLED(SPINDLE_FEATURE)
+ //#define SPINDLE_CHANGE_DIR // Enable if your spindle controller can change spindle direction
+ #define SPINDLE_CHANGE_DIR_STOP // Enable if the spindle should stop before changing spin direction
+ #define SPINDLE_INVERT_DIR false // Set to "true" if the spin direction is reversed
+
+ #define SPINDLE_LASER_POWERUP_DELAY 5000 // (ms) Delay to allow the spindle/laser to come up to speed/power
+ #define SPINDLE_LASER_POWERDOWN_DELAY 5000 // (ms) Delay to allow the spindle to stop
+
+ /**
+ * M3/M4 Power Equation
+ *
+ * Each tool uses different value ranges for speed / power control.
+ * These parameters are used to convert between tool power units and PWM.
+ *
+ * Speed/Power = (PWMDC / 255 * 100 - SPEED_POWER_INTERCEPT) / SPEED_POWER_SLOPE
+ * PWMDC = (spdpwr - SPEED_POWER_MIN) / (SPEED_POWER_MAX - SPEED_POWER_MIN) / SPEED_POWER_SLOPE
+ */
+ #if ENABLED(SPINDLE_LASER_USE_PWM)
+ #define SPEED_POWER_INTERCEPT 0 // (%) 0-100 i.e., Minimum power percentage
+ #define SPEED_POWER_MIN 5000 // (RPM)
+ #define SPEED_POWER_MAX 30000 // (RPM) SuperPID router controller 0 - 30,000 RPM
+ #define SPEED_POWER_STARTUP 25000 // (RPM) M3/M4 speed/power default (with no arguments)
+ #endif
+
+ #else
+
+ #if ENABLED(SPINDLE_LASER_USE_PWM)
+ #define SPEED_POWER_INTERCEPT 0 // (%) 0-100 i.e., Minimum power percentage
+ #define SPEED_POWER_MIN 0 // (%) 0-100
+ #define SPEED_POWER_MAX 100 // (%) 0-100
+ #define SPEED_POWER_STARTUP 80 // (%) M3/M4 speed/power default (with no arguments)
+ #endif
+
+ // Define the minimum and maximum test pulse time values for a laser test fire function
+ #define LASER_TEST_PULSE_MIN 1 // (ms) Used with Laser Control Menu
+ #define LASER_TEST_PULSE_MAX 999 // (ms) Caution: Menu may not show more than 3 characters
+
+ #define SPINDLE_LASER_POWERUP_DELAY 50 // (ms) Delay to allow the spindle/laser to come up to speed/power
+ #define SPINDLE_LASER_POWERDOWN_DELAY 50 // (ms) Delay to allow the spindle to stop
+
+ /**
+ * Laser Safety Timeout
+ *
+ * The laser should be turned off when there is no movement for a period of time.
+ * Consider material flammability, cut rate, and G-code order when setting this
+ * value. Too low and it could turn off during a very slow move; too high and
+ * the material could ignite.
+ */
+ #define LASER_SAFETY_TIMEOUT_MS 1000 // (ms)
+
+ /**
+ * Any M3 or G1/2/3/5 command with the 'I' parameter enables continuous inline power mode.
+ *
+ * e.g., 'M3 I' enables continuous inline power which is processed by the planner.
+ * Power is stored in move blocks and applied when blocks are processed by the Stepper ISR.
+ *
+ * 'M4 I' sets dynamic mode which uses the current feedrate to calculate a laser power OCR value.
+ *
+ * Any move in dynamic mode will use the current feedrate to calculate the laser power.
+ * Feed rates are set by the F parameter of a move command e.g. G1 X0 Y10 F6000
+ * Laser power would be calculated by bit shifting off 8 LSB's. In binary this is div 256.
+ * The calculation gives us ocr values from 0 to 255, values over F65535 will be set as 255 .
+ * More refined power control such as compesation for accell/decell will be addressed in future releases.
+ *
+ * M5 I clears inline mode and set power to 0, M5 sets the power output to 0 but leaves inline mode on.
+ */
+
+ /**
+ * Enable M3 commands for laser mode inline power planner syncing.
+ * This feature enables any M3 S-value to be injected into the block buffers while in
+ * CUTTER_MODE_CONTINUOUS. The option allows M3 laser power to be commited without waiting
+ * for a planner syncronization
+ */
+ //#define LASER_POWER_SYNC
+
+ /**
+ * Scale the laser's power in proportion to the movement rate.
+ *
+ * - Sets the entry power proportional to the entry speed over the nominal speed.
+ * - Ramps the power up every N steps to approximate the speed trapezoid.
+ * - Due to the limited power resolution this is only approximate.
+ */
+ //#define LASER_POWER_TRAP
+
+ //
+ // Laser I2C Ammeter (High precision INA226 low/high side module)
+ //
+ //#define I2C_AMMETER
+ #if ENABLED(I2C_AMMETER)
+ #define I2C_AMMETER_IMAX 0.1 // (Amps) Calibration value for the expected current range
+ #define I2C_AMMETER_SHUNT_RESISTOR 0.1 // (Ohms) Calibration shunt resistor value
+ #endif
+
+ //
+ // Laser Coolant Flow Meter
+ //
+ //#define LASER_COOLANT_FLOW_METER
+ #if ENABLED(LASER_COOLANT_FLOW_METER)
+ #define FLOWMETER_PIN 20 // Requires an external interrupt-enabled pin (e.g., RAMPS 2,3,18,19,20,21)
+ #define FLOWMETER_PPL 5880 // (pulses/liter) Flow meter pulses-per-liter on the input pin
+ #define FLOWMETER_INTERVAL 1000 // (ms) Flow rate calculation interval in milliseconds
+ #define FLOWMETER_SAFETY // Prevent running the laser without the minimum flow rate set below
+ #if ENABLED(FLOWMETER_SAFETY)
+ #define FLOWMETER_MIN_LITERS_PER_MINUTE 1.5 // (liters/min) Minimum flow required when enabled
+ #endif
+ #endif
+
+ #endif
+#endif // SPINDLE_FEATURE || LASER_FEATURE
+
+/**
+ * Synchronous Laser Control with M106/M107
+ *
+ * Marlin normally applies M106/M107 fan speeds at a time "soon after" processing
+ * a planner block. This is too inaccurate for a PWM/TTL laser attached to the fan
+ * header (as with some add-on laser kits). Enable this option to set fan/laser
+ * speeds with much more exact timing for improved print fidelity.
+ *
+ * NOTE: This option sacrifices some cooling fan speed options.
+ */
+//#define LASER_SYNCHRONOUS_M106_M107
+
+/**
+ * Coolant Control
+ *
+ * Add the M7, M8, and M9 commands to turn mist or flood coolant on and off.
+ *
+ * Note: COOLANT_MIST_PIN and/or COOLANT_FLOOD_PIN must also be defined.
+ */
+//#define COOLANT_CONTROL
+#if ENABLED(COOLANT_CONTROL)
+ #define COOLANT_MIST // Enable if mist coolant is present
+ #define COOLANT_FLOOD // Enable if flood coolant is present
+ #define COOLANT_MIST_INVERT false // Set "true" if the on/off function is reversed
+ #define COOLANT_FLOOD_INVERT false // Set "true" if the on/off function is reversed
+#endif
+
+// @section filament width
+
+/**
+ * Filament Width Sensor
+ *
+ * Measures the filament width in real-time and adjusts
+ * flow rate to compensate for any irregularities.
+ *
+ * Also allows the measured filament diameter to set the
+ * extrusion rate, so the slicer only has to specify the
+ * volume.
+ *
+ * Only a single extruder is supported at this time.
+ *
+ * 34 RAMPS_14 : Analog input 5 on the AUX2 connector
+ * 81 PRINTRBOARD : Analog input 2 on the Exp1 connector (version B,C,D,E)
+ * 301 RAMBO : Analog input 3
+ *
+ * Note: May require analog pins to be defined for other boards.
+ */
+//#define FILAMENT_WIDTH_SENSOR
+
+#if ENABLED(FILAMENT_WIDTH_SENSOR)
+ #define FILAMENT_SENSOR_EXTRUDER_NUM 0 // Index of the extruder that has the filament sensor. :[0,1,2,3,4]
+ #define MEASUREMENT_DELAY_CM 14 // (cm) The distance from the filament sensor to the melting chamber
+
+ #define FILWIDTH_ERROR_MARGIN 1.0 // (mm) If a measurement differs too much from nominal width ignore it
+ #define MAX_MEASUREMENT_DELAY 20 // (bytes) Buffer size for stored measurements (1 byte per cm). Must be larger than MEASUREMENT_DELAY_CM.
+
+ #define DEFAULT_MEASURED_FILAMENT_DIA DEFAULT_NOMINAL_FILAMENT_DIA // Set measured to nominal initially
+
+ // Display filament width on the LCD status line. Status messages will expire after 5 seconds.
+ //#define FILAMENT_LCD_DISPLAY
+#endif
+
+// @section power
+
+/**
+ * Power Monitor
+ * Monitor voltage (V) and/or current (A), and -when possible- power (W)
+ *
+ * Read and configure with M430
+ *
+ * The current sensor feeds DC voltage (relative to the measured current) to an analog pin
+ * The voltage sensor feeds DC voltage (relative to the measured voltage) to an analog pin
+ */
+//#define POWER_MONITOR_CURRENT // Monitor the system current
+//#define POWER_MONITOR_VOLTAGE // Monitor the system voltage
+
+#if ENABLED(POWER_MONITOR_CURRENT)
+ #define POWER_MONITOR_VOLTS_PER_AMP 0.05000 // Input voltage to the MCU analog pin per amp - DO NOT apply more than ADC_VREF!
+ #define POWER_MONITOR_CURRENT_OFFSET 0 // Offset (in amps) applied to the calculated current
+ #define POWER_MONITOR_FIXED_VOLTAGE 13.6 // Voltage for a current sensor with no voltage sensor (for power display)
+#endif
+
+#if ENABLED(POWER_MONITOR_VOLTAGE)
+ #define POWER_MONITOR_VOLTS_PER_VOLT 0.077933 // Input voltage to the MCU analog pin per volt - DO NOT apply more than ADC_VREF!
+ #define POWER_MONITOR_VOLTAGE_OFFSET 0 // Offset (in volts) applied to the calculated voltage
+#endif
+
+// @section safety
+
+/**
+ * Stepper Driver Anti-SNAFU Protection
+ *
+ * If the SAFE_POWER_PIN is defined for your board, Marlin will check
+ * that stepper drivers are properly plugged in before applying power.
+ * Disable protection if your stepper drivers don't support the feature.
+ */
+//#define DISABLE_DRIVER_SAFE_POWER_PROTECT
+
+// @section cnc
+
+/**
+ * CNC Coordinate Systems
+ *
+ * Enables G53 and G54-G59.3 commands to select coordinate systems
+ * and G92.1 to reset the workspace to native machine space.
+ */
+//#define CNC_COORDINATE_SYSTEMS
+
+// @section reporting
+
+/**
+ * Auto-report fan speed with M123 S
+ * Requires fans with tachometer pins
+ */
+//#define AUTO_REPORT_FANS
+
+/**
+ * Auto-report temperatures with M155 S
+ */
+#define AUTO_REPORT_TEMPERATURES
+#if ENABLED(AUTO_REPORT_TEMPERATURES) && TEMP_SENSOR_REDUNDANT
+ //#define AUTO_REPORT_REDUNDANT // Include the "R" sensor in the auto-report
+#endif
+
+/**
+ * Auto-report position with M154 S
+ */
+//#define AUTO_REPORT_POSITION
+
+/**
+ * Include capabilities in M115 output
+ */
+#define EXTENDED_CAPABILITIES_REPORT
+#if ENABLED(EXTENDED_CAPABILITIES_REPORT)
+ #define M115_GEOMETRY_REPORT // MRiscoC Enabled
+#endif
+
+// @section security
+
+/**
+ * Expected Printer Check
+ * Add the M16 G-code to compare a string to the MACHINE_NAME.
+ * M16 with a non-matching string causes the printer to halt.
+ */
+//#define EXPECTED_PRINTER_CHECK
+
+// @section volumetrics
+
+/**
+ * Disable all Volumetric extrusion options
+ */
+//#define NO_VOLUMETRICS
+
+#if DISABLED(NO_VOLUMETRICS)
+ /**
+ * Volumetric extrusion default state
+ * Activate to make volumetric extrusion the default method,
+ * with DEFAULT_NOMINAL_FILAMENT_DIA as the default diameter.
+ *
+ * M200 D0 to disable, M200 Dn to set a new diameter (and enable volumetric).
+ * M200 S0/S1 to disable/enable volumetric extrusion.
+ */
+ //#define VOLUMETRIC_DEFAULT_ON
+
+ //#define VOLUMETRIC_EXTRUDER_LIMIT
+ #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT)
+ /**
+ * Default volumetric extrusion limit in cubic mm per second (mm^3/sec).
+ * This factory setting applies to all extruders.
+ * Use 'M200 [T] L' to override and 'M502' to reset.
+ * A non-zero value activates Volume-based Extrusion Limiting.
+ */
+ #define DEFAULT_VOLUMETRIC_EXTRUDER_LIMIT 0.00 // (mm^3/sec)
+ #endif
+#endif
+
+// @section reporting
+
+// Extra options for the M114 "Current Position" report
+//#define M114_DETAIL // Use 'M114` for details to check planner calculations
+//#define M114_REALTIME // Real current position based on forward kinematics
+//#define M114_LEGACY // M114 used to synchronize on every call. Enable if needed.
+
+//#define REPORT_FAN_CHANGE // Report the new fan speed when changed by M106 (and others)
+
+// @section gcode
+
+/**
+ * Spend 28 bytes of SRAM to optimize the G-code parser
+ */
+#define FASTER_GCODE_PARSER
+
+#if ENABLED(FASTER_GCODE_PARSER)
+ //#define GCODE_QUOTED_STRINGS // Support for quoted string parameters
+#endif
+
+// Support for MeatPack G-code compression (https://github.com/scottmudge/OctoPrint-MeatPack)
+//#define MEATPACK_ON_SERIAL_PORT_1
+//#define MEATPACK_ON_SERIAL_PORT_2
+
+//#define GCODE_CASE_INSENSITIVE // Accept G-code sent to the firmware in lowercase
+
+//#define REPETIER_GCODE_M360 // Add commands originally from Repetier FW
+
+/**
+ * Enable this option for a leaner build of Marlin that removes all
+ * workspace offsets, simplifying coordinate transformations, leveling, etc.
+ *
+ * - M206 and M428 are disabled.
+ * - G92 will revert to its behavior from Marlin 1.0.
+ */
+#define NO_WORKSPACE_OFFSETS // MRiscoC Save flash space and simplify movements
+
+/**
+ * CNC G-code options
+ * Support CNC-style G-code dialects used by laser cutters, drawing machine cams, etc.
+ * Note that G0 feedrates should be used with care for 3D printing (if used at all).
+ * High feedrates may cause ringing and harm print quality.
+ */
+//#define PAREN_COMMENTS // Support for parentheses-delimited comments
+//#define GCODE_MOTION_MODES // Remember the motion mode (G0 G1 G2 G3 G5 G38.X) and apply for X Y Z E F, etc.
+
+// Enable and set a (default) feedrate for all G0 moves
+//#define G0_FEEDRATE 3000 // (mm/min)
+#ifdef G0_FEEDRATE
+ //#define VARIABLE_G0_FEEDRATE // The G0 feedrate is set by F in G0 motion mode
+#endif
+
+// @section gcode
+
+/**
+ * Startup commands
+ *
+ * Execute certain G-code commands immediately after power-on.
+ */
+//#define STARTUP_COMMANDS "M17 Z"
+
+/**
+ * G-code Macros
+ *
+ * Add G-codes M810-M819 to define and run G-code macros.
+ * Macros are not saved to EEPROM.
+ */
+//#define GCODE_MACROS
+#if ENABLED(GCODE_MACROS)
+ #define GCODE_MACROS_SLOTS 5 // Up to 10 may be used
+ #define GCODE_MACROS_SLOT_SIZE 50 // Maximum length of a single macro
+#endif
+
+/**
+ * User-defined menu items to run custom G-code.
+ * Up to 25 may be defined, but the actual number is LCD-dependent.
+ */
+
+// @section custom main menu
+
+// Custom Menu: Main Menu
+//#define CUSTOM_MENU_MAIN
+#if ENABLED(CUSTOM_MENU_MAIN)
+ //#define CUSTOM_MENU_MAIN_TITLE "Custom Commands"
+ #define CUSTOM_MENU_MAIN_SCRIPT_DONE "M117 User Script Done"
+ #define CUSTOM_MENU_MAIN_SCRIPT_AUDIBLE_FEEDBACK
+ //#define CUSTOM_MENU_MAIN_SCRIPT_RETURN // Return to status screen after a script
+ #define CUSTOM_MENU_MAIN_ONLY_IDLE // Only show custom menu when the machine is idle
+
+ #define MAIN_MENU_ITEM_1_DESC "Home & UBL Info"
+ #define MAIN_MENU_ITEM_1_GCODE "G28\nG29 W"
+ //#define MAIN_MENU_ITEM_1_CONFIRM // Show a confirmation dialog before this action
+
+ #define MAIN_MENU_ITEM_2_DESC "Preheat for " PREHEAT_1_LABEL
+ #define MAIN_MENU_ITEM_2_GCODE "M140 S" STRINGIFY(PREHEAT_1_TEMP_BED) "\nM104 S" STRINGIFY(PREHEAT_1_TEMP_HOTEND)
+ //#define MAIN_MENU_ITEM_2_CONFIRM
+
+ //#define MAIN_MENU_ITEM_3_DESC "Preheat for " PREHEAT_2_LABEL
+ //#define MAIN_MENU_ITEM_3_GCODE "M140 S" STRINGIFY(PREHEAT_2_TEMP_BED) "\nM104 S" STRINGIFY(PREHEAT_2_TEMP_HOTEND)
+ //#define MAIN_MENU_ITEM_3_CONFIRM
+
+ //#define MAIN_MENU_ITEM_4_DESC "Heat Bed/Home/Level"
+ //#define MAIN_MENU_ITEM_4_GCODE "M140 S" STRINGIFY(PREHEAT_2_TEMP_BED) "\nG28\nG29"
+ //#define MAIN_MENU_ITEM_4_CONFIRM
+
+ //#define MAIN_MENU_ITEM_5_DESC "Home & Info"
+ //#define MAIN_MENU_ITEM_5_GCODE "G28\nM503"
+ //#define MAIN_MENU_ITEM_5_CONFIRM
+#endif
+
+// @section custom config menu
+
+// Custom Menu: Configuration Menu
+//#define CUSTOM_MENU_CONFIG
+#if ENABLED(CUSTOM_MENU_CONFIG)
+ //#define CUSTOM_MENU_CONFIG_TITLE "Custom Commands"
+ #define CUSTOM_MENU_CONFIG_SCRIPT_DONE "M117 Wireless Script Done"
+ #define CUSTOM_MENU_CONFIG_SCRIPT_AUDIBLE_FEEDBACK
+ //#define CUSTOM_MENU_CONFIG_SCRIPT_RETURN // Return to status screen after a script
+ #define CUSTOM_MENU_CONFIG_ONLY_IDLE // Only show custom menu when the machine is idle
+
+ #define CONFIG_MENU_ITEM_1_DESC "Wifi ON"
+ #define CONFIG_MENU_ITEM_1_GCODE "M118 [ESP110] WIFI-STA pwd=12345678"
+ //#define CONFIG_MENU_ITEM_1_CONFIRM // Show a confirmation dialog before this action
+
+ #define CONFIG_MENU_ITEM_2_DESC "Bluetooth ON"
+ #define CONFIG_MENU_ITEM_2_GCODE "M118 [ESP110] BT pwd=12345678"
+ //#define CONFIG_MENU_ITEM_2_CONFIRM
+
+ //#define CONFIG_MENU_ITEM_3_DESC "Radio OFF"
+ //#define CONFIG_MENU_ITEM_3_GCODE "M118 [ESP110] OFF pwd=12345678"
+ //#define CONFIG_MENU_ITEM_3_CONFIRM
+
+ //#define CONFIG_MENU_ITEM_4_DESC "Wifi ????"
+ //#define CONFIG_MENU_ITEM_4_GCODE "M118 ????"
+ //#define CONFIG_MENU_ITEM_4_CONFIRM
+
+ //#define CONFIG_MENU_ITEM_5_DESC "Wifi ????"
+ //#define CONFIG_MENU_ITEM_5_GCODE "M118 ????"
+ //#define CONFIG_MENU_ITEM_5_CONFIRM
+#endif
+
+// @section custom buttons
+
+/**
+ * User-defined buttons to run custom G-code.
+ * Up to 25 may be defined.
+ */
+//#define CUSTOM_USER_BUTTONS
+#if ENABLED(CUSTOM_USER_BUTTONS)
+ //#define BUTTON1_PIN -1
+ #if PIN_EXISTS(BUTTON1)
+ #define BUTTON1_HIT_STATE LOW // State of the triggered button. NC=LOW. NO=HIGH.
+ #define BUTTON1_WHEN_PRINTING false // Button allowed to trigger during printing?
+ #define BUTTON1_GCODE "G28"
+ #define BUTTON1_DESC "Homing" // Optional string to set the LCD status
+ #endif
+
+ //#define BUTTON2_PIN -1
+ #if PIN_EXISTS(BUTTON2)
+ #define BUTTON2_HIT_STATE LOW
+ #define BUTTON2_WHEN_PRINTING false
+ #define BUTTON2_GCODE "M140 S" STRINGIFY(PREHEAT_1_TEMP_BED) "\nM104 S" STRINGIFY(PREHEAT_1_TEMP_HOTEND)
+ #define BUTTON2_DESC "Preheat for " PREHEAT_1_LABEL
+ #endif
+
+ //#define BUTTON3_PIN -1
+ #if PIN_EXISTS(BUTTON3)
+ #define BUTTON3_HIT_STATE LOW
+ #define BUTTON3_WHEN_PRINTING false
+ #define BUTTON3_GCODE "M140 S" STRINGIFY(PREHEAT_2_TEMP_BED) "\nM104 S" STRINGIFY(PREHEAT_2_TEMP_HOTEND)
+ #define BUTTON3_DESC "Preheat for " PREHEAT_2_LABEL
+ #endif
+#endif
+
+// @section host
+
+/**
+ * Host Action Commands
+ *
+ * Define host streamer action commands in compliance with the standard.
+ *
+ * See https://reprap.org/wiki/G-code#Action_commands
+ * Common commands ........ poweroff, pause, paused, resume, resumed, cancel
+ * G29_RETRY_AND_RECOVER .. probe_rewipe, probe_failed
+ *
+ * Some features add reason codes to extend these commands.
+ *
+ * Host Prompt Support enables Marlin to use the host for user prompts so
+ * filament runout and other processes can be managed from the host side.
+ */
+#define HOST_ACTION_COMMANDS // MRiscoC Enabled actions from host
+#if ENABLED(HOST_ACTION_COMMANDS)
+ //#define HOST_PAUSE_M76 // Tell the host to pause in response to M76
+ #define HOST_PROMPT_SUPPORT // Initiate host prompts to get user feedback // MRiscoC Enabled responses from host
+ #if ENABLED(HOST_PROMPT_SUPPORT)
+ //#define HOST_STATUS_NOTIFICATIONS // Send some status messages to the host as notifications
+ #endif
+ //#define HOST_START_MENU_ITEM // Add a menu item that tells the host to start
+ //#define HOST_SHUTDOWN_MENU_ITEM // Add a menu item that tells the host to shut down
+#endif
+
+// @section extras
+
+/**
+ * Cancel Objects
+ *
+ * Implement M486 to allow Marlin to skip objects
+ */
+#define CANCEL_OBJECTS // MRiscoC Enabled M486 to skip objects
+#if ENABLED(CANCEL_OBJECTS)
+ #define CANCEL_OBJECTS_REPORTING // Emit the current object as a status message
+#endif
+
+/**
+ * I2C position encoders for closed loop control.
+ * Developed by Chris Barr at Aus3D.
+ *
+ * Wiki: https://wiki.aus3d.com.au/Magnetic_Encoder
+ * Github: https://github.com/Aus3D/MagneticEncoder
+ *
+ * Supplier: https://aus3d.com.au/magnetic-encoder-module
+ * Alternative Supplier: https://reliabuild3d.com/
+ *
+ * Reliabuild encoders have been modified to improve reliability.
+ * @section i2c encoders
+ */
+
+//#define I2C_POSITION_ENCODERS
+#if ENABLED(I2C_POSITION_ENCODERS)
+
+ #define I2CPE_ENCODER_CNT 1 // The number of encoders installed; max of 5
+ // encoders supported currently.
+
+ #define I2CPE_ENC_1_ADDR I2CPE_PRESET_ADDR_X // I2C address of the encoder. 30-200.
+ #define I2CPE_ENC_1_AXIS X_AXIS // Axis the encoder module is installed on. _AXIS.
+ #define I2CPE_ENC_1_TYPE I2CPE_ENC_TYPE_LINEAR // Type of encoder: I2CPE_ENC_TYPE_LINEAR -or-
+ // I2CPE_ENC_TYPE_ROTARY.
+ #define I2CPE_ENC_1_TICKS_UNIT 2048 // 1024 for magnetic strips with 2mm poles; 2048 for
+ // 1mm poles. For linear encoders this is ticks / mm,
+ // for rotary encoders this is ticks / revolution.
+ //#define I2CPE_ENC_1_TICKS_REV (16 * 200) // Only needed for rotary encoders; number of stepper
+ // steps per full revolution (motor steps/rev * microstepping)
+ //#define I2CPE_ENC_1_INVERT // Invert the direction of axis travel.
+ #define I2CPE_ENC_1_EC_METHOD I2CPE_ECM_MICROSTEP // Type of error error correction.
+ #define I2CPE_ENC_1_EC_THRESH 0.10 // Threshold size for error (in mm) above which the
+ // printer will attempt to correct the error; errors
+ // smaller than this are ignored to minimize effects of
+ // measurement noise / latency (filter).
+
+ #define I2CPE_ENC_2_ADDR I2CPE_PRESET_ADDR_Y // Same as above, but for encoder 2.
+ #define I2CPE_ENC_2_AXIS Y_AXIS
+ #define I2CPE_ENC_2_TYPE I2CPE_ENC_TYPE_LINEAR
+ #define I2CPE_ENC_2_TICKS_UNIT 2048
+ //#define I2CPE_ENC_2_TICKS_REV (16 * 200)
+ //#define I2CPE_ENC_2_INVERT
+ #define I2CPE_ENC_2_EC_METHOD I2CPE_ECM_MICROSTEP
+ #define I2CPE_ENC_2_EC_THRESH 0.10
+
+ #define I2CPE_ENC_3_ADDR I2CPE_PRESET_ADDR_Z // Encoder 3. Add additional configuration options
+ #define I2CPE_ENC_3_AXIS Z_AXIS // as above, or use defaults below.
+
+ #define I2CPE_ENC_4_ADDR I2CPE_PRESET_ADDR_E // Encoder 4.
+ #define I2CPE_ENC_4_AXIS E_AXIS
+
+ #define I2CPE_ENC_5_ADDR 34 // Encoder 5.
+ #define I2CPE_ENC_5_AXIS E_AXIS
+
+ // Default settings for encoders which are enabled, but without settings configured above.
+ #define I2CPE_DEF_TYPE I2CPE_ENC_TYPE_LINEAR
+ #define I2CPE_DEF_ENC_TICKS_UNIT 2048
+ #define I2CPE_DEF_TICKS_REV (16 * 200)
+ #define I2CPE_DEF_EC_METHOD I2CPE_ECM_NONE
+ #define I2CPE_DEF_EC_THRESH 0.1
+
+ //#define I2CPE_ERR_THRESH_ABORT 100.0 // Threshold size for error (in mm) error on any given
+ // axis after which the printer will abort. Comment out to
+ // disable abort behavior.
+
+ #define I2CPE_TIME_TRUSTED 10000 // After an encoder fault, there must be no further fault
+ // for this amount of time (in ms) before the encoder
+ // is trusted again.
+
+ /**
+ * Position is checked every time a new command is executed from the buffer but during long moves,
+ * this setting determines the minimum update time between checks. A value of 100 works well with
+ * error rolling average when attempting to correct only for skips and not for vibration.
+ */
+ #define I2CPE_MIN_UPD_TIME_MS 4 // (ms) Minimum time between encoder checks.
+
+ // Use a rolling average to identify persistent errors that indicate skips, as opposed to vibration and noise.
+ #define I2CPE_ERR_ROLLING_AVERAGE
+
+#endif // I2C_POSITION_ENCODERS
+
+/**
+ * Analog Joystick(s)
+ * @section joystick
+ */
+//#define JOYSTICK
+#if ENABLED(JOYSTICK)
+ #define JOY_X_PIN 5 // RAMPS: Suggested pin A5 on AUX2
+ #define JOY_Y_PIN 10 // RAMPS: Suggested pin A10 on AUX2
+ #define JOY_Z_PIN 12 // RAMPS: Suggested pin A12 on AUX2
+ #define JOY_EN_PIN 44 // RAMPS: Suggested pin D44 on AUX2
+
+ //#define INVERT_JOY_X // Enable if X direction is reversed
+ //#define INVERT_JOY_Y // Enable if Y direction is reversed
+ //#define INVERT_JOY_Z // Enable if Z direction is reversed
+
+ // Use M119 with JOYSTICK_DEBUG to find reasonable values after connecting:
+ #define JOY_X_LIMITS { 5600, 8190-100, 8190+100, 10800 } // min, deadzone start, deadzone end, max
+ #define JOY_Y_LIMITS { 5600, 8250-100, 8250+100, 11000 }
+ #define JOY_Z_LIMITS { 4800, 8080-100, 8080+100, 11550 }
+ //#define JOYSTICK_DEBUG
+#endif
+
+/**
+ * Mechanical Gantry Calibration
+ * Modern replacement for the Průša TMC_Z_CALIBRATION.
+ * Adds capability to work with any adjustable current drivers.
+ * Implemented as G34 because M915 is deprecated.
+ * @section calibrate
+ */
+//#define MECHANICAL_GANTRY_CALIBRATION
+#if ENABLED(MECHANICAL_GANTRY_CALIBRATION)
+ #define GANTRY_CALIBRATION_CURRENT 600 // Default calibration current in ma
+ #define GANTRY_CALIBRATION_EXTRA_HEIGHT 15 // Extra distance in mm past Z_###_POS to move
+ #define GANTRY_CALIBRATION_FEEDRATE 500 // Feedrate for correction move
+ //#define GANTRY_CALIBRATION_TO_MIN // Enable to calibrate Z in the MIN direction
+
+ //#define GANTRY_CALIBRATION_SAFE_POSITION XY_CENTER // Safe position for nozzle
+ //#define GANTRY_CALIBRATION_XY_PARK_FEEDRATE 3000 // XY Park Feedrate - MMM
+ //#define GANTRY_CALIBRATION_COMMANDS_PRE ""
+ #define GANTRY_CALIBRATION_COMMANDS_POST "G28" // G28 highly recommended to ensure an accurate position
+#endif
+
+/**
+ * Instant freeze / unfreeze functionality
+ * Potentially useful for emergency stop that allows being resumed.
+ * @section interface
+ */
+//#define FREEZE_FEATURE
+#if ENABLED(FREEZE_FEATURE)
+ //#define FREEZE_PIN 41 // Override the default (KILL) pin here
+ #define FREEZE_STATE LOW // State of pin indicating freeze
+#endif
+
+/**
+ * MAX7219 Debug Matrix
+ *
+ * Add support for a low-cost 8x8 LED Matrix based on the Max7219 chip as a realtime status display.
+ * Requires 3 signal wires. Some useful debug options are included to demonstrate its usage.
+ * @section debug matrix
+ */
+//#define MAX7219_DEBUG
+#if ENABLED(MAX7219_DEBUG)
+ #define MAX7219_CLK_PIN 64
+ #define MAX7219_DIN_PIN 57
+ #define MAX7219_LOAD_PIN 44
+
+ //#define MAX7219_GCODE // Add the M7219 G-code to control the LED matrix
+ #define MAX7219_INIT_TEST 2 // Test pattern at startup: 0=none, 1=sweep, 2=spiral
+ #define MAX7219_NUMBER_UNITS 1 // Number of Max7219 units in chain.
+ #define MAX7219_ROTATE 0 // Rotate the display clockwise (in multiples of +/- 90°)
+ // connector at: right=0 bottom=-90 top=90 left=180
+ //#define MAX7219_REVERSE_ORDER // The order of the LED matrix units may be reversed
+ //#define MAX7219_REVERSE_EACH // The LEDs in each matrix unit row may be reversed
+ //#define MAX7219_SIDE_BY_SIDE // Big chip+matrix boards can be chained side-by-side
+
+ /**
+ * Sample debug features
+ * If you add more debug displays, be careful to avoid conflicts!
+ */
+ #define MAX7219_DEBUG_PRINTER_ALIVE // Blink corner LED of 8x8 matrix to show that the firmware is functioning
+ #define MAX7219_DEBUG_PLANNER_HEAD 2 // Show the planner queue head position on this and the next LED matrix row
+ #define MAX7219_DEBUG_PLANNER_TAIL 4 // Show the planner queue tail position on this and the next LED matrix row
+
+ #define MAX7219_DEBUG_PLANNER_QUEUE 0 // Show the current planner queue depth on this and the next LED matrix row
+ // If you experience stuttering, reboots, etc. this option can reveal how
+ // tweaks made to the configuration are affecting the printer in real-time.
+ #define MAX7219_DEBUG_PROFILE 6 // Display the fraction of CPU time spent in profiled code on this LED matrix
+ // row. By default idle() is profiled so this shows how "idle" the processor is.
+ // See class CodeProfiler.
+#endif
+
+/**
+ * NanoDLP Sync support
+ *
+ * Support for Synchronized Z moves when used with NanoDLP. G0/G1 axis moves will
+ * output a "Z_move_comp" string to enable synchronization with DLP projector exposure.
+ * This feature allows you to use [[WaitForDoneMessage]] instead of M400 commands.
+ * @section nanodlp
+ */
+//#define NANODLP_Z_SYNC
+#if ENABLED(NANODLP_Z_SYNC)
+ //#define NANODLP_ALL_AXIS // Send a "Z_move_comp" report for any axis move (not just Z).
+#endif
+
+/**
+ * Ethernet. Use M552 to enable and set the IP address.
+ * @section network
+ */
+#if HAS_ETHERNET
+ #define MAC_ADDRESS { 0xDE, 0xAD, 0xBE, 0xEF, 0xF0, 0x0D } // A MAC address unique to your network
+#endif
+
+/**
+ * WiFi Support (Espressif ESP32 WiFi)
+ */
+//#define WIFISUPPORT // Marlin embedded WiFi management
+//#define ESP3D_WIFISUPPORT // ESP3D Library WiFi management (https://github.com/luc-github/ESP3DLib)
+
+#if EITHER(WIFISUPPORT, ESP3D_WIFISUPPORT)
+ //#define WEBSUPPORT // Start a webserver (which may include auto-discovery)
+ //#define OTASUPPORT // Support over-the-air firmware updates
+ //#define WIFI_CUSTOM_COMMAND // Accept feature config commands (e.g., WiFi ESP3D) from the host
+
+ /**
+ * To set a default WiFi SSID / Password, create a file called Configuration_Secure.h with
+ * the following defines, customized for your network. This specific file is excluded via
+ * .gitignore to prevent it from accidentally leaking to the public.
+ *
+ * #define WIFI_SSID "WiFi SSID"
+ * #define WIFI_PWD "WiFi Password"
+ */
+ //#include "Configuration_Secure.h" // External file with WiFi SSID / Password
+#endif
+
+// @section multi-material
+
+/**
+ * Průša Multi-Material Unit (MMU)
+ * Enable in Configuration.h
+ *
+ * These devices allow a single stepper driver on the board to drive
+ * multi-material feeders with any number of stepper motors.
+ */
+#if HAS_PRUSA_MMU1
+ /**
+ * This option only allows the multiplexer to switch on tool-change.
+ * Additional options to configure custom E moves are pending.
+ *
+ * Override the default DIO selector pins here, if needed.
+ * Some pins files may provide defaults for these pins.
+ */
+ //#define E_MUX0_PIN 40 // Always Required
+ //#define E_MUX1_PIN 42 // Needed for 3 to 8 inputs
+ //#define E_MUX2_PIN 44 // Needed for 5 to 8 inputs
+#elif HAS_PRUSA_MMU2
+ // Serial port used for communication with MMU2.
+ #define MMU2_SERIAL_PORT 2
+
+ // Use hardware reset for MMU if a pin is defined for it
+ //#define MMU2_RST_PIN 23
+
+ // Enable if the MMU2 has 12V stepper motors (MMU2 Firmware 1.0.2 and up)
+ //#define MMU2_MODE_12V
+
+ // G-code to execute when MMU2 F.I.N.D.A. probe detects filament runout
+ #define MMU2_FILAMENT_RUNOUT_SCRIPT "M600"
+
+ // Add an LCD menu for MMU2
+ //#define MMU2_MENUS
+ #if EITHER(MMU2_MENUS, HAS_PRUSA_MMU2S)
+ // Settings for filament load / unload from the LCD menu.
+ // This is for Průša MK3-style extruders. Customize for your hardware.
+ #define MMU2_FILAMENTCHANGE_EJECT_FEED 80.0
+ #define MMU2_LOAD_TO_NOZZLE_SEQUENCE \
+ { 7.2, 1145 }, \
+ { 14.4, 871 }, \
+ { 36.0, 1393 }, \
+ { 14.4, 871 }, \
+ { 50.0, 198 }
+
+ #define MMU2_RAMMING_SEQUENCE \
+ { 1.0, 1000 }, \
+ { 1.0, 1500 }, \
+ { 2.0, 2000 }, \
+ { 1.5, 3000 }, \
+ { 2.5, 4000 }, \
+ { -15.0, 5000 }, \
+ { -14.0, 1200 }, \
+ { -6.0, 600 }, \
+ { 10.0, 700 }, \
+ { -10.0, 400 }, \
+ { -50.0, 2000 }
+ #endif
+
+ /**
+ * Using a sensor like the MMU2S
+ * This mode requires a MK3S extruder with a sensor at the extruder idler, like the MMU2S.
+ * See https://help.prusa3d.com/en/guide/3b-mk3s-mk2-5s-extruder-upgrade_41560, step 11
+ */
+ #if HAS_PRUSA_MMU2S
+ #define MMU2_C0_RETRY 5 // Number of retries (total time = timeout*retries)
+
+ #define MMU2_CAN_LOAD_FEEDRATE 800 // (mm/min)
+ #define MMU2_CAN_LOAD_SEQUENCE \
+ { 0.1, MMU2_CAN_LOAD_FEEDRATE }, \
+ { 60.0, MMU2_CAN_LOAD_FEEDRATE }, \
+ { -52.0, MMU2_CAN_LOAD_FEEDRATE }
+
+ #define MMU2_CAN_LOAD_RETRACT 6.0 // (mm) Keep under the distance between Load Sequence values
+ #define MMU2_CAN_LOAD_DEVIATION 0.8 // (mm) Acceptable deviation
+
+ #define MMU2_CAN_LOAD_INCREMENT 0.2 // (mm) To reuse within MMU2 module
+ #define MMU2_CAN_LOAD_INCREMENT_SEQUENCE \
+ { -MMU2_CAN_LOAD_INCREMENT, MMU2_CAN_LOAD_FEEDRATE }
+
+ #else
+
+ /**
+ * MMU1 Extruder Sensor
+ *
+ * Support for a Průša (or other) IR Sensor to detect filament near the extruder
+ * and make loading more reliable. Suitable for an extruder equipped with a filament
+ * sensor less than 38mm from the gears.
+ *
+ * During loading the extruder will stop when the sensor is triggered, then do a last
+ * move up to the gears. If no filament is detected, the MMU2 can make some more attempts.
+ * If all attempts fail, a filament runout will be triggered.
+ */
+ //#define MMU_EXTRUDER_SENSOR
+ #if ENABLED(MMU_EXTRUDER_SENSOR)
+ #define MMU_LOADING_ATTEMPTS_NR 5 // max. number of attempts to load filament if first load fail
+ #endif
+
+ #endif
+
+ //#define MMU2_DEBUG // Write debug info to serial output
+
+#endif // HAS_PRUSA_MMU2
+
+/**
+ * Advanced Print Counter settings
+ * @section stats
+ */
+#if ENABLED(PRINTCOUNTER)
+ #define SERVICE_WARNING_BUZZES 3
+ // Activate up to 3 service interval watchdogs
+ //#define SERVICE_NAME_1 "Service S"
+ //#define SERVICE_INTERVAL_1 100 // print hours
+ //#define SERVICE_NAME_2 "Service L"
+ //#define SERVICE_INTERVAL_2 200 // print hours
+ //#define SERVICE_NAME_3 "Service 3"
+ //#define SERVICE_INTERVAL_3 1 // print hours
+#endif
+
+// @section develop
+
+//
+// M100 Free Memory Watcher to debug memory usage
+//
+//#define M100_FREE_MEMORY_WATCHER
+
+//
+// M42 - Set pin states
+//
+//#define DIRECT_PIN_CONTROL
+
+//
+// M43 - display pin status, toggle pins, watch pins, watch endstops & toggle LED, test servo probe
+//
+//#define PINS_DEBUGGING
+
+// Enable Tests that will run at startup and produce a report
+//#define MARLIN_TEST_BUILD
+
+// Enable Marlin dev mode which adds some special commands
+//#define MARLIN_DEV_MODE
+
+#if ENABLED(MARLIN_DEV_MODE)
+ /**
+ * D576 - Buffer Monitoring
+ * To help diagnose print quality issues stemming from empty command buffers.
+ */
+ //#define BUFFER_MONITORING
+#endif
+
+/**
+ * Postmortem Debugging captures misbehavior and outputs the CPU status and backtrace to serial.
+ * When running in the debugger it will break for debugging. This is useful to help understand
+ * a crash from a remote location. Requires ~400 bytes of SRAM and 5Kb of flash.
+ */
+//#define POSTMORTEM_DEBUGGING
+
+/**
+ * Software Reset options
+ */
+//#define SOFT_RESET_VIA_SERIAL // 'KILL' and '^X' commands will soft-reset the controller
+//#define SOFT_RESET_ON_KILL // Use a digital button to soft-reset the controller after KILL
+
+// Report uncleaned reset reason from register r2 instead of MCUSR. Supported by Optiboot on AVR.
+//#define OPTIBOOT_RESET_REASON
diff --git a/Marlin/Makefile b/Marlin/Makefile
new file mode 100644
index 0000000000..ca7cacaa6a
--- /dev/null
+++ b/Marlin/Makefile
@@ -0,0 +1,1037 @@
+# Marlin Firmware Arduino Project Makefile
+#
+# Makefile Based on:
+# Arduino 0011 Makefile
+# Arduino adaptation by mellis, eighthave, oli.keller
+# Marlin adaption by Daid
+# Marlin 2.0 support and RELOC_WORKAROUND by @marcio-ao
+#
+# This has been tested with Arduino 0022.
+#
+# This makefile allows you to build sketches from the command line
+# without the Arduino environment (or Java).
+#
+# Detailed instructions for using the makefile:
+#
+# 1. Modify the line containing "ARDUINO_INSTALL_DIR" to point to the directory that
+# contains the Arduino installation (for example, under macOS, this
+# might be /Applications/Arduino.app/Contents/Resources/Java).
+#
+# 2. Modify the line containing "UPLOAD_PORT" to refer to the filename
+# representing the USB or serial connection to your Arduino board
+# (e.g. UPLOAD_PORT = /dev/tty.USB0). If the exact name of this file
+# changes, you can use * as a wild card (e.g. UPLOAD_PORT = /dev/tty.usb*).
+#
+# 3. Set the line containing "MCU" to match your board's processor. Set
+# "PROG_MCU" as the AVR part name corresponding to "MCU". You can use the
+# following command to get a list of correspondences: `avrdude -c alf -p x`
+# Older boards are atmega8 based, newer ones like Arduino Mini, Bluetooth
+# or Diecimila have the atmega168. If you're using a LilyPad Arduino,
+# change F_CPU to 8000000. If you are using Gen7 electronics, you
+# probably need to use 20000000. Either way, you must regenerate
+# the speed lookup table with create_speed_lookuptable.py.
+#
+# 4. Type "make" and press enter to compile/verify your program.
+#
+# 5. Type "make upload", reset your Arduino board, and press enter to
+# upload your program to the Arduino board.
+#
+# Note that all settings at the top of this file can be overridden from
+# the command line with, for example, "make HARDWARE_MOTHERBOARD=71"
+#
+# To compile for RAMPS (atmega2560) with Arduino 1.6.9 at root/arduino you would use...
+#
+# make ARDUINO_VERSION=10609 AVR_TOOLS_PATH=/root/arduino/hardware/tools/avr/bin/ \
+# HARDWARE_MOTHERBOARD=1200 ARDUINO_INSTALL_DIR=/root/arduino
+#
+# To compile and upload simply add "upload" to the end of the line...
+#
+# make ARDUINO_VERSION=10609 AVR_TOOLS_PATH=/root/arduino/hardware/tools/avr/bin/ \
+# HARDWARE_MOTHERBOARD=1200 ARDUINO_INSTALL_DIR=/root/arduino upload
+#
+# If uploading doesn't work try adding the parameter "AVRDUDE_PROGRAMMER=wiring" or
+# start upload manually (using stk500) like so:
+#
+# avrdude -C /root/arduino/hardware/tools/avr/etc/avrdude.conf -v -p m2560 -c stk500 \
+# -U flash:w:applet/Marlin.hex:i -P /dev/ttyUSB0
+#
+# Or, try disconnecting USB to power down and then reconnecting before running avrdude.
+#
+
+# This defines the board to compile for (see boards.h for your board's ID)
+HARDWARE_MOTHERBOARD ?= 1020
+
+ifeq ($(OS),Windows_NT)
+ # Windows
+ ARDUINO_INSTALL_DIR ?= ${HOME}/Arduino
+ ARDUINO_USER_DIR ?= ${HOME}/Arduino
+else
+ UNAME_S := $(shell uname -s)
+ ifeq ($(UNAME_S),Linux)
+ # Linux
+ ARDUINO_INSTALL_DIR ?= /usr/share/arduino
+ ARDUINO_USER_DIR ?= ${HOME}/Arduino
+ endif
+ ifeq ($(UNAME_S),Darwin)
+ # Darwin (macOS)
+ ARDUINO_INSTALL_DIR ?= /Applications/Arduino.app/Contents/Java
+ ARDUINO_USER_DIR ?= ${HOME}/Documents/Arduino
+ AVR_TOOLS_PATH ?= /Applications/Arduino.app/Contents/Java/hardware/tools/avr/bin/
+ endif
+endif
+
+# Arduino source install directory, and version number
+# On most linuxes this will be /usr/share/arduino
+ARDUINO_INSTALL_DIR ?= ${HOME}/Arduino
+ARDUINO_VERSION ?= 106
+
+# The installed Libraries are in the User folder
+ARDUINO_USER_DIR ?= ${HOME}/Arduino
+
+# You can optionally set a path to the avr-gcc tools.
+# Requires a trailing slash. For example, /usr/local/avr-gcc/bin/
+AVR_TOOLS_PATH ?=
+
+# Programmer configuration
+UPLOAD_RATE ?= 57600
+AVRDUDE_PROGRAMMER ?= arduino
+# On most linuxes this will be /dev/ttyACM0 or /dev/ttyACM1
+UPLOAD_PORT ?= /dev/ttyUSB0
+
+# Directory used to build files in, contains all the build files, from object
+# files to the final hex file on linux it is best to put an absolute path
+# like /home/username/tmp .
+BUILD_DIR ?= applet
+
+# This defines whether Liquid_TWI2 support will be built
+LIQUID_TWI2 ?= 0
+
+# This defines if Wire is needed
+WIRE ?= 0
+
+# This defines if Tone is needed (i.e., SPEAKER is defined in Configuration.h)
+# Disabling this (and SPEAKER) saves approximately 350 bytes of memory.
+TONE ?= 1
+
+# This defines if U8GLIB is needed (may require RELOC_WORKAROUND)
+U8GLIB ?= 0
+
+# This defines whether to include the Trinamic TMCStepper library
+TMC ?= 0
+
+# This defines whether to include the AdaFruit NeoPixel library
+NEOPIXEL ?= 0
+
+############
+# Try to automatically determine whether RELOC_WORKAROUND is needed based
+# on GCC versions:
+# https://www.avrfreaks.net/comment/1789106#comment-1789106
+
+CC_MAJ:=$(shell $(CC) -dM -E - < /dev/null | grep __GNUC__ | cut -f3 -d\ )
+CC_MIN:=$(shell $(CC) -dM -E - < /dev/null | grep __GNUC_MINOR__ | cut -f3 -d\ )
+CC_PATCHLEVEL:=$(shell $(CC) -dM -E - < /dev/null | grep __GNUC_PATCHLEVEL__ | cut -f3 -d\ )
+CC_VER:=$(shell echo $$(( $(CC_MAJ) * 10000 + $(CC_MIN) * 100 + $(CC_PATCHLEVEL) )))
+ifeq ($(shell test $(CC_VER) -lt 40901 && echo 1),1)
+ $(warning This GCC version $(CC_VER) is likely broken. Enabling relocation workaround.)
+ RELOC_WORKAROUND = 1
+endif
+
+############################################################################
+# Below here nothing should be changed...
+
+# Here the Arduino variant is selected by the board type
+# HARDWARE_VARIANT = "arduino", "Sanguino", "Gen7", ...
+# MCU = "atmega1280", "Mega2560", "atmega2560", "atmega644p", ...
+
+ifeq ($(HARDWARE_MOTHERBOARD),0)
+
+ # No motherboard selected
+
+#
+# RAMPS 1.3 / 1.4 - ATmega1280, ATmega2560
+#
+
+# MEGA/RAMPS up to 1.2
+else ifeq ($(HARDWARE_MOTHERBOARD),1000)
+
+# RAMPS 1.3 (Power outputs: Hotend, Fan, Bed)
+else ifeq ($(HARDWARE_MOTHERBOARD),1010)
+# RAMPS 1.3 (Power outputs: Hotend0, Hotend1, Bed)
+else ifeq ($(HARDWARE_MOTHERBOARD),1011)
+# RAMPS 1.3 (Power outputs: Hotend, Fan0, Fan1)
+else ifeq ($(HARDWARE_MOTHERBOARD),1012)
+# RAMPS 1.3 (Power outputs: Hotend0, Hotend1, Fan)
+else ifeq ($(HARDWARE_MOTHERBOARD),1013)
+# RAMPS 1.3 (Power outputs: Spindle, Controller Fan)
+else ifeq ($(HARDWARE_MOTHERBOARD),1014)
+
+# RAMPS 1.4 (Power outputs: Hotend, Fan, Bed)
+else ifeq ($(HARDWARE_MOTHERBOARD),1020)
+# RAMPS 1.4 (Power outputs: Hotend0, Hotend1, Bed)
+else ifeq ($(HARDWARE_MOTHERBOARD),1021)
+# RAMPS 1.4 (Power outputs: Hotend, Fan0, Fan1)
+else ifeq ($(HARDWARE_MOTHERBOARD),1022)
+# RAMPS 1.4 (Power outputs: Hotend0, Hotend1, Fan)
+else ifeq ($(HARDWARE_MOTHERBOARD),1023)
+# RAMPS 1.4 (Power outputs: Spindle, Controller Fan)
+else ifeq ($(HARDWARE_MOTHERBOARD),1024)
+
+# RAMPS Plus 3DYMY (Power outputs: Hotend, Fan, Bed)
+else ifeq ($(HARDWARE_MOTHERBOARD),1030)
+# RAMPS Plus 3DYMY (Power outputs: Hotend0, Hotend1, Bed)
+else ifeq ($(HARDWARE_MOTHERBOARD),1031)
+# RAMPS Plus 3DYMY (Power outputs: Hotend, Fan0, Fan1)
+else ifeq ($(HARDWARE_MOTHERBOARD),1032)
+# RAMPS Plus 3DYMY (Power outputs: Hotend0, Hotend1, Fan)
+else ifeq ($(HARDWARE_MOTHERBOARD),1033)
+# RAMPS Plus 3DYMY (Power outputs: Spindle, Controller Fan)
+else ifeq ($(HARDWARE_MOTHERBOARD),1034)
+
+#
+# RAMPS Derivatives - ATmega1280, ATmega2560
+#
+
+# 3Drag Controller
+else ifeq ($(HARDWARE_MOTHERBOARD),1100)
+# Velleman K8200 Controller (derived from 3Drag Controller)
+else ifeq ($(HARDWARE_MOTHERBOARD),1101)
+# Velleman K8400 Controller (derived from 3Drag Controller)
+else ifeq ($(HARDWARE_MOTHERBOARD),1102)
+# Velleman K8600 Controller (Vertex Nano)
+else ifeq ($(HARDWARE_MOTHERBOARD),1103)
+# Velleman K8800 Controller (Vertex Delta)
+else ifeq ($(HARDWARE_MOTHERBOARD),1104)
+# 2PrintBeta BAM&DICE with STK drivers
+else ifeq ($(HARDWARE_MOTHERBOARD),1105)
+# 2PrintBeta BAM&DICE Due with STK drivers
+else ifeq ($(HARDWARE_MOTHERBOARD),1106)
+# MKS BASE v1.0
+else ifeq ($(HARDWARE_MOTHERBOARD),1107)
+# MKS BASE v1.4 with Allegro A4982 stepper drivers
+else ifeq ($(HARDWARE_MOTHERBOARD),1108)
+# MKS BASE v1.5 with Allegro A4982 stepper drivers
+else ifeq ($(HARDWARE_MOTHERBOARD),1109)
+# MKS BASE v1.6 with Allegro A4982 stepper drivers
+else ifeq ($(HARDWARE_MOTHERBOARD),1110)
+# MKS BASE 1.0 with Heroic HR4982 stepper drivers
+else ifeq ($(HARDWARE_MOTHERBOARD),1111)
+# MKS GEN v1.3 or 1.4
+else ifeq ($(HARDWARE_MOTHERBOARD),1112)
+# MKS GEN L
+else ifeq ($(HARDWARE_MOTHERBOARD),1113)
+# BigTreeTech or BIQU KFB2.0
+else ifeq ($(HARDWARE_MOTHERBOARD),1114)
+# zrib V2.0 (Chinese RAMPS replica)
+else ifeq ($(HARDWARE_MOTHERBOARD),1115)
+# zrib V5.2 (Chinese RAMPS replica)
+else ifeq ($(HARDWARE_MOTHERBOARD),1116)
+# Felix 2.0+ Electronics Board (RAMPS like)
+else ifeq ($(HARDWARE_MOTHERBOARD),1117)
+# Invent-A-Part RigidBoard
+else ifeq ($(HARDWARE_MOTHERBOARD),1118)
+# Invent-A-Part RigidBoard V2
+else ifeq ($(HARDWARE_MOTHERBOARD),1119)
+# Sainsmart 2-in-1 board
+else ifeq ($(HARDWARE_MOTHERBOARD),1120)
+# Ultimaker
+else ifeq ($(HARDWARE_MOTHERBOARD),1121)
+# Ultimaker (Older electronics. Pre 1.5.4. This is rare)
+else ifeq ($(HARDWARE_MOTHERBOARD),1122)
+ MCU ?= atmega1280
+ PROG_MCU ?= m1280
+# Azteeg X3
+else ifeq ($(HARDWARE_MOTHERBOARD),1123)
+# Azteeg X3 Pro
+else ifeq ($(HARDWARE_MOTHERBOARD),1124)
+# Ultimainboard 2.x (Uses TEMP_SENSOR 20)
+else ifeq ($(HARDWARE_MOTHERBOARD),1125)
+# Rumba
+else ifeq ($(HARDWARE_MOTHERBOARD),1126)
+# Raise3D N series Rumba derivative
+else ifeq ($(HARDWARE_MOTHERBOARD),1127)
+# Rapide Lite 200 (v1, low-cost RUMBA clone with drv)
+else ifeq ($(HARDWARE_MOTHERBOARD),1128)
+# Formbot T-Rex 2 Plus
+else ifeq ($(HARDWARE_MOTHERBOARD),1129)
+# Formbot T-Rex 3
+else ifeq ($(HARDWARE_MOTHERBOARD),1130)
+# Formbot Raptor
+else ifeq ($(HARDWARE_MOTHERBOARD),1131)
+# Formbot Raptor 2
+else ifeq ($(HARDWARE_MOTHERBOARD),1132)
+# bq ZUM Mega 3D
+else ifeq ($(HARDWARE_MOTHERBOARD),1133)
+# MakeBoard Mini v2.1.2 by MicroMake
+else ifeq ($(HARDWARE_MOTHERBOARD),1134)
+# TriGorilla Anycubic version 1.3-based on RAMPS EFB
+else ifeq ($(HARDWARE_MOTHERBOARD),1135)
+# ... Ver 1.4
+else ifeq ($(HARDWARE_MOTHERBOARD),1136)
+# ... Rev 1.1 (new servo pin order)
+else ifeq ($(HARDWARE_MOTHERBOARD),1137)
+# Creality: Ender-4, CR-8
+else ifeq ($(HARDWARE_MOTHERBOARD),1138)
+# Creality: CR10S, CR20, CR-X
+else ifeq ($(HARDWARE_MOTHERBOARD),1139)
+# Dagoma F5
+else ifeq ($(HARDWARE_MOTHERBOARD),1140)
+# FYSETC F6 1.3
+else ifeq ($(HARDWARE_MOTHERBOARD),1141)
+# FYSETC F6 1.4
+else ifeq ($(HARDWARE_MOTHERBOARD),1142)
+# Wanhao Duplicator i3 Plus
+else ifeq ($(HARDWARE_MOTHERBOARD),1143)
+# VORON Design
+else ifeq ($(HARDWARE_MOTHERBOARD),1144)
+# Tronxy TRONXY-V3-1.0
+else ifeq ($(HARDWARE_MOTHERBOARD),1145)
+# Z-Bolt X Series
+else ifeq ($(HARDWARE_MOTHERBOARD),1146)
+# TT OSCAR
+else ifeq ($(HARDWARE_MOTHERBOARD),1147)
+# Overlord/Overlord Pro
+else ifeq ($(HARDWARE_MOTHERBOARD),1148)
+# ADIMLab Gantry v1
+else ifeq ($(HARDWARE_MOTHERBOARD),1149)
+# ADIMLab Gantry v2
+else ifeq ($(HARDWARE_MOTHERBOARD),1150)
+# BIQU Tango V1
+else ifeq ($(HARDWARE_MOTHERBOARD),1151)
+# MKS GEN L V2
+else ifeq ($(HARDWARE_MOTHERBOARD),1152)
+# MKS GEN L V2.1
+else ifeq ($(HARDWARE_MOTHERBOARD),1153)
+# Copymaster 3D
+else ifeq ($(HARDWARE_MOTHERBOARD),1154)
+# Ortur 4
+else ifeq ($(HARDWARE_MOTHERBOARD),1155)
+# Tenlog D3 Hero IDEX printer
+else ifeq ($(HARDWARE_MOTHERBOARD),1156)
+# Tenlog D3,5,6 Pro IDEX printers
+else ifeq ($(HARDWARE_MOTHERBOARD),1157)
+# Ramps S 1.2 by Sakul.cz (Power outputs: Hotend0, Hotend1, Fan, Bed)
+else ifeq ($(HARDWARE_MOTHERBOARD),1158)
+# Ramps S 1.2 by Sakul.cz (Power outputs: Hotend0, Hotend1, Hotend2, Bed)
+else ifeq ($(HARDWARE_MOTHERBOARD),1159)
+# Ramps S 1.2 by Sakul.cz (Power outputs: Hotend, Fan0, Fan1, Bed)
+else ifeq ($(HARDWARE_MOTHERBOARD),1160)
+# Longer LK1 PRO / Alfawise U20 Pro (PRO version)
+else ifeq ($(HARDWARE_MOTHERBOARD),1161)
+# Longer LKx PRO / Alfawise Uxx Pro (PRO version)
+else ifeq ($(HARDWARE_MOTHERBOARD),1162)
+# Zonestar zrib V5.3 (Chinese RAMPS replica)
+else ifeq ($(HARDWARE_MOTHERBOARD),1163)
+# Pxmalion Core I3
+else ifeq ($(HARDWARE_MOTHERBOARD),1164)
+
+#
+# RAMBo and derivatives
+#
+
+# Rambo
+else ifeq ($(HARDWARE_MOTHERBOARD),1200)
+# Mini-Rambo
+else ifeq ($(HARDWARE_MOTHERBOARD),1201)
+# Mini-Rambo 1.0a
+else ifeq ($(HARDWARE_MOTHERBOARD),1202)
+# Einsy Rambo
+else ifeq ($(HARDWARE_MOTHERBOARD),1203)
+# Einsy Retro
+else ifeq ($(HARDWARE_MOTHERBOARD),1204)
+# abee Scoovo X9H
+else ifeq ($(HARDWARE_MOTHERBOARD),1205)
+# Rambo ThinkerV2
+else ifeq ($(HARDWARE_MOTHERBOARD),1206)
+
+#
+# Other ATmega1280, ATmega2560
+#
+
+# Cartesio CN Controls V11
+else ifeq ($(HARDWARE_MOTHERBOARD),1300)
+# Cartesio CN Controls V12
+else ifeq ($(HARDWARE_MOTHERBOARD),1301)
+# Cartesio CN Controls V15
+else ifeq ($(HARDWARE_MOTHERBOARD),1302)
+# Cheaptronic v1.0
+else ifeq ($(HARDWARE_MOTHERBOARD),1303)
+# Cheaptronic v2.0
+else ifeq ($(HARDWARE_MOTHERBOARD),1304)
+# Makerbot Mightyboard Revision E
+else ifeq ($(HARDWARE_MOTHERBOARD),1305)
+# Megatronics
+else ifeq ($(HARDWARE_MOTHERBOARD),1306)
+# Megatronics v2.0
+else ifeq ($(HARDWARE_MOTHERBOARD),1307)
+# Megatronics v3.0
+else ifeq ($(HARDWARE_MOTHERBOARD),1308)
+# Megatronics v3.1
+else ifeq ($(HARDWARE_MOTHERBOARD),1309)
+# Megatronics v3.2
+else ifeq ($(HARDWARE_MOTHERBOARD),1310)
+# Elefu Ra Board (v3)
+else ifeq ($(HARDWARE_MOTHERBOARD),1311)
+# Leapfrog
+else ifeq ($(HARDWARE_MOTHERBOARD),1312)
+# Mega controller
+else ifeq ($(HARDWARE_MOTHERBOARD),1313)
+# Geeetech GT2560 Rev A
+else ifeq ($(HARDWARE_MOTHERBOARD),1314)
+# Geeetech GT2560 Rev A+ (with auto level probe)
+else ifeq ($(HARDWARE_MOTHERBOARD),1315)
+# Geeetech GT2560 Rev B
+else ifeq ($(HARDWARE_MOTHERBOARD),1316)
+# Geeetech GT2560 Rev B for A10(M/T/D)
+else ifeq ($(HARDWARE_MOTHERBOARD),1317)
+# Geeetech GT2560 Rev B for A10(M/T/D)
+else ifeq ($(HARDWARE_MOTHERBOARD),1318)
+# Geeetech GT2560 Rev B for Mecreator2
+else ifeq ($(HARDWARE_MOTHERBOARD),1319)
+# Geeetech GT2560 Rev B for A20(M/T/D)
+else ifeq ($(HARDWARE_MOTHERBOARD),1320)
+# Einstart retrofit
+else ifeq ($(HARDWARE_MOTHERBOARD),1321)
+# Wanhao 0ne+ i3 Mini
+else ifeq ($(HARDWARE_MOTHERBOARD),1322)
+# Leapfrog Xeed 2015
+else ifeq ($(HARDWARE_MOTHERBOARD),1323)
+# PICA Shield (original version)
+else ifeq ($(HARDWARE_MOTHERBOARD),1324)
+# PICA Shield (rev C or later)
+else ifeq ($(HARDWARE_MOTHERBOARD),1325)
+# Intamsys 4.0 (Funmat HT)
+else ifeq ($(HARDWARE_MOTHERBOARD),1326)
+# Malyan M180 Mainboard Version 2 (no display function, direct G-code only)
+else ifeq ($(HARDWARE_MOTHERBOARD),1327)
+# Geeetech GT2560 Rev B for A20(M/T/D)
+else ifeq ($(HARDWARE_MOTHERBOARD),1328)
+# Mega controller & Protoneer CNC Shield V3.00
+else ifeq ($(HARDWARE_MOTHERBOARD),1329)
+
+#
+# ATmega1281, ATmega2561
+#
+
+# Minitronics v1.0/1.1
+else ifeq ($(HARDWARE_MOTHERBOARD),1400)
+ MCU ?= atmega1281
+ PROG_MCU ?= m1281
+# Silvergate v1.0
+else ifeq ($(HARDWARE_MOTHERBOARD),1401)
+ MCU ?= atmega1281
+ PROG_MCU ?= m1281
+
+#
+# Sanguinololu and Derivatives - ATmega644P, ATmega1284P
+#
+
+# Sanguinololu < 1.2
+else ifeq ($(HARDWARE_MOTHERBOARD),1500)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega644p
+ PROG_MCU ?= m644p
+# Sanguinololu 1.2 and above
+else ifeq ($(HARDWARE_MOTHERBOARD),1501)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega644p
+ PROG_MCU ?= m644p
+# Melzi
+else ifeq ($(HARDWARE_MOTHERBOARD),1502)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega644p
+ PROG_MCU ?= m644p
+# Melzi V2.0
+else ifeq ($(HARDWARE_MOTHERBOARD),1503)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega1284p
+ PROG_MCU ?= m1284p
+# Melzi with ATmega1284 (MaKr3d version)
+else ifeq ($(HARDWARE_MOTHERBOARD),1504)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega1284p
+ PROG_MCU ?= m1284p
+# Melzi Creality3D board (for CR-10 etc)
+else ifeq ($(HARDWARE_MOTHERBOARD),1505)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega1284p
+ PROG_MCU ?= m1284p
+# Melzi Malyan M150 board
+else ifeq ($(HARDWARE_MOTHERBOARD),1506)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega1284p
+ PROG_MCU ?= m1284p
+# Tronxy X5S
+else ifeq ($(HARDWARE_MOTHERBOARD),1507)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega1284p
+ PROG_MCU ?= m1284p
+# STB V1.1
+else ifeq ($(HARDWARE_MOTHERBOARD),1508)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega1284p
+ PROG_MCU ?= m1284p
+# Azteeg X1
+else ifeq ($(HARDWARE_MOTHERBOARD),1509)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega1284p
+ PROG_MCU ?= m1284p
+# Anet 1.0 (Melzi clone)
+else ifeq ($(HARDWARE_MOTHERBOARD),1510)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega1284p
+ PROG_MCU ?= m1284p
+# ZoneStar ZMIB V2
+else ifeq ($(HARDWARE_MOTHERBOARD),1511)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega1284p
+ PROG_MCU ?= m1284p
+
+#
+# Other ATmega644P, ATmega644, ATmega1284P
+#
+
+# Gen3 Monolithic Electronics
+else ifeq ($(HARDWARE_MOTHERBOARD),1600)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega644p
+ PROG_MCU ?= m644p
+# Gen3+
+else ifeq ($(HARDWARE_MOTHERBOARD),1601)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega644p
+ PROG_MCU ?= m644p
+# Gen6
+else ifeq ($(HARDWARE_MOTHERBOARD),1602)
+ HARDWARE_VARIANT ?= Gen6
+ MCU ?= atmega644p
+ PROG_MCU ?= m644p
+# Gen6 deluxe
+else ifeq ($(HARDWARE_MOTHERBOARD),1603)
+ HARDWARE_VARIANT ?= Gen6
+ MCU ?= atmega644p
+ PROG_MCU ?= m644p
+# Gen7 custom (Alfons3 Version)
+else ifeq ($(HARDWARE_MOTHERBOARD),1604)
+ HARDWARE_VARIANT ?= Gen7
+ MCU ?= atmega644
+ PROG_MCU ?= m644
+ F_CPU ?= 20000000
+# Gen7 v1.1, v1.2
+else ifeq ($(HARDWARE_MOTHERBOARD),1605)
+ HARDWARE_VARIANT ?= Gen7
+ MCU ?= atmega644p
+ PROG_MCU ?= m644p
+ F_CPU ?= 20000000
+# Gen7 v1.3
+else ifeq ($(HARDWARE_MOTHERBOARD),1606)
+ HARDWARE_VARIANT ?= Gen7
+ MCU ?= atmega644p
+ PROG_MCU ?= m644p
+ F_CPU ?= 20000000
+# Gen7 v1.4
+else ifeq ($(HARDWARE_MOTHERBOARD),1607)
+ HARDWARE_VARIANT ?= Gen7
+ MCU ?= atmega1284p
+ PROG_MCU ?= m1284p
+ F_CPU ?= 20000000
+# Alpha OMCA board
+else ifeq ($(HARDWARE_MOTHERBOARD),1608)
+ HARDWARE_VARIANT ?= SanguinoA
+ MCU ?= atmega644
+ PROG_MCU ?= m644
+# Final OMCA board
+else ifeq ($(HARDWARE_MOTHERBOARD),1609)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega644p
+ PROG_MCU ?= m644p
+# Sethi 3D_1
+else ifeq ($(HARDWARE_MOTHERBOARD),1610)
+ HARDWARE_VARIANT ?= Sanguino
+ MCU ?= atmega644p
+ PROG_MCU ?= m644p
+
+#
+# Teensyduino - AT90USB1286, AT90USB1286P
+#
+
+# Teensylu
+else ifeq ($(HARDWARE_MOTHERBOARD),1700)
+ HARDWARE_VARIANT ?= Teensy
+ MCU ?= at90usb1286
+ PROG_MCU ?= usb1286
+# Printrboard (AT90USB1286)
+else ifeq ($(HARDWARE_MOTHERBOARD),1701)
+ HARDWARE_VARIANT ?= Teensy
+ MCU ?= at90usb1286
+ PROG_MCU ?= usb1286
+# Printrboard Revision F (AT90USB1286)
+else ifeq ($(HARDWARE_MOTHERBOARD),1702)
+ HARDWARE_VARIANT ?= Teensy
+ MCU ?= at90usb1286
+ PROG_MCU ?= usb1286
+# Brainwave (AT90USB646)
+else ifeq ($(HARDWARE_MOTHERBOARD),1703)
+ HARDWARE_VARIANT ?= Teensy
+ MCU ?= at90usb646
+ PROG_MCU ?= usb646
+# Brainwave Pro (AT90USB1286)
+else ifeq ($(HARDWARE_MOTHERBOARD),1704)
+ HARDWARE_VARIANT ?= Teensy
+ MCU ?= at90usb1286
+ PROG_MCU ?= usb1286
+# SAV Mk-I (AT90USB1286)
+else ifeq ($(HARDWARE_MOTHERBOARD),1705)
+ HARDWARE_VARIANT ?= Teensy
+ MCU ?= at90usb1286
+ PROG_MCU ?= usb1286
+# Teensy++2.0 (AT90USB1286)
+else ifeq ($(HARDWARE_MOTHERBOARD),1706)
+ HARDWARE_VARIANT ?= Teensy
+ MCU ?= at90usb1286
+ PROG_MCU ?= usb1286
+# 5DPrint D8 Driver Board
+else ifeq ($(HARDWARE_MOTHERBOARD),1707)
+ HARDWARE_VARIANT ?= Teensy
+ MCU ?= at90usb1286
+ PROG_MCU ?= usb1286
+
+# UltiMachine Archim1 (with DRV8825 drivers)
+else ifeq ($(HARDWARE_MOTHERBOARD),3023)
+ HARDWARE_VARIANT ?= archim
+ MCPU = cortex-m3
+ F_CPU = 84000000
+ IS_MCU = 0
+# UltiMachine Archim2 (with TMC2130 drivers)
+else ifeq ($(HARDWARE_MOTHERBOARD),3024)
+ HARDWARE_VARIANT ?= archim
+ MCPU = cortex-m3
+ F_CPU = 84000000
+ IS_MCU = 0
+endif
+
+# Be sure to regenerate speed_lookuptable.h with create_speed_lookuptable.py
+# if you are setting this to something other than 16MHz
+# Do not put the UL suffix, it's done later on.
+# Set to 16Mhz if not yet set.
+F_CPU ?= 16000000
+
+# Set to microcontroller if IS_MCU not yet set
+IS_MCU ?= 1
+
+ifeq ($(IS_MCU),1)
+ # Set to arduino, ATmega2560 if not yet set.
+ HARDWARE_VARIANT ?= arduino
+ MCU ?= atmega2560
+ PROG_MCU ?= m2560
+
+ TOOL_PREFIX = avr
+ MCU_FLAGS = -mmcu=$(MCU)
+ SIZE_FLAGS = --mcu=$(MCU) -C
+else
+ TOOL_PREFIX = arm-none-eabi
+ CPU_FLAGS = -mthumb -mcpu=$(MCPU)
+ SIZE_FLAGS = -A
+endif
+
+# Arduino contained the main source code for the Arduino
+# Libraries, the "hardware variant" are for boards
+# that derives from that, and their source are present in
+# the main Marlin source directory
+
+TARGET = $(notdir $(CURDIR))
+
+# VPATH tells make to look into these directory for source files,
+# there is no need to specify explicit pathnames as long as the
+# directory is added here
+
+# The Makefile for previous versions of Marlin used VPATH for all
+# source files, but for Marlin 2.0, we use VPATH only for arduino
+# library files.
+
+VPATH = .
+VPATH += $(BUILD_DIR)
+VPATH += $(HARDWARE_SRC)
+
+ifeq ($(HARDWARE_VARIANT), $(filter $(HARDWARE_VARIANT),arduino Teensy Sanguino))
+ # Old libraries (avr-core 1.6.21 < / Arduino < 1.6.8)
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/arduino/avr/libraries/SPI
+ # New libraries (avr-core >= 1.6.21 / Arduino >= 1.6.8)
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/arduino/avr/libraries/SPI/src
+endif
+
+ifeq ($(IS_MCU),1)
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/arduino/avr/cores/arduino
+
+ # Old libraries (avr-core 1.6.21 < / Arduino < 1.6.8)
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/arduino/avr/libraries/SPI
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/arduino/avr/libraries/SoftwareSerial
+ # New libraries (avr-core >= 1.6.21 / Arduino >= 1.6.8)
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/arduino/avr/libraries/SPI/src
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/arduino/avr/libraries/SoftwareSerial/src
+endif
+
+VPATH += $(ARDUINO_INSTALL_DIR)/libraries/LiquidCrystal/src
+
+ifeq ($(LIQUID_TWI2), 1)
+ WIRE = 1
+ VPATH += $(ARDUINO_INSTALL_DIR)/libraries/LiquidTWI2
+endif
+ifeq ($(WIRE), 1)
+ # Old libraries (avr-core 1.6.21 / Arduino < 1.6.8)
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/arduino/avr/libraries/Wire
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/arduino/avr/libraries/Wire/utility
+ # New libraries (avr-core >= 1.6.21 / Arduino >= 1.6.8)
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/arduino/avr/libraries/Wire/src
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/arduino/avr/libraries/Wire/src/utility
+endif
+ifeq ($(NEOPIXEL), 1)
+VPATH += $(ARDUINO_INSTALL_DIR)/libraries/Adafruit_NeoPixel
+endif
+ifeq ($(U8GLIB), 1)
+VPATH += $(ARDUINO_INSTALL_DIR)/libraries/U8glib
+VPATH += $(ARDUINO_INSTALL_DIR)/libraries/U8glib/csrc
+VPATH += $(ARDUINO_INSTALL_DIR)/libraries/U8glib/cppsrc
+VPATH += $(ARDUINO_INSTALL_DIR)/libraries/U8glib/fntsrc
+endif
+ifeq ($(TMC), 1)
+VPATH += $(ARDUINO_INSTALL_DIR)/libraries/TMCStepper/src
+VPATH += $(ARDUINO_INSTALL_DIR)/libraries/TMCStepper/src/source
+endif
+
+ifeq ($(HARDWARE_VARIANT), arduino)
+ HARDWARE_SUB_VARIANT ?= mega
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/arduino/avr/variants/$(HARDWARE_SUB_VARIANT)
+else ifeq ($(HARDWARE_VARIANT), Sanguino)
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/marlin/avr/variants/sanguino
+else ifeq ($(HARDWARE_VARIANT), archim)
+ VPATH += $(ARDUINO_INSTALL_DIR)/packages/ultimachine/hardware/sam/1.6.9-b/system/libsam
+ VPATH += $(ARDUINO_INSTALL_DIR)/packages/ultimachine/hardware/sam/1.6.9-b/system/CMSIS/CMSIS/Include/
+ VPATH += $(ARDUINO_INSTALL_DIR)/packages/ultimachine/hardware/sam/1.6.9-b/system/CMSIS/Device/ATMEL/
+ VPATH += $(ARDUINO_INSTALL_DIR)/packages/ultimachine/hardware/sam/1.6.9-b/cores/arduino
+ VPATH += $(ARDUINO_INSTALL_DIR)/packages/ultimachine/hardware/sam/1.6.9-b/cores/arduino/avr
+ VPATH += $(ARDUINO_INSTALL_DIR)/packages/ultimachine/hardware/sam/1.6.9-b/cores/arduino/USB
+ VPATH += $(ARDUINO_INSTALL_DIR)/packages/ultimachine/hardware/sam/1.6.9-b/libraries/Wire/src
+ VPATH += $(ARDUINO_INSTALL_DIR)/packages/ultimachine/hardware/sam/1.6.9-b/libraries/SPI/src
+ VPATH += $(ARDUINO_INSTALL_DIR)/packages/ultimachine/hardware/sam/1.6.9-b/libraries/U8glib/src/clib
+ VPATH += $(ARDUINO_INSTALL_DIR)/packages/ultimachine/hardware/sam/1.6.9-b/variants/archim
+ LDSCRIPT = $(ARDUINO_INSTALL_DIR)/packages/ultimachine/hardware/sam/1.6.9-b/variants/archim/linker_scripts/gcc/flash.ld
+ LDLIBS = $(ARDUINO_INSTALL_DIR)/packages/ultimachine/hardware/sam/1.6.9-b/variants/archim/libsam_sam3x8e_gcc_rel.a
+else
+ HARDWARE_SUB_VARIANT ?= standard
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/$(HARDWARE_VARIANT)/variants/$(HARDWARE_SUB_VARIANT)
+endif
+
+LIB_SRC = wiring.c \
+ wiring_analog.c wiring_digital.c \
+ wiring_shift.c WInterrupts.c hooks.c
+
+ifeq ($(HARDWARE_VARIANT), archim)
+ LIB_ASRC += wiring_pulse_asm.S
+else
+ LIB_SRC += wiring_pulse.c
+endif
+
+ifeq ($(HARDWARE_VARIANT), Teensy)
+ LIB_SRC = wiring.c
+ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/teensy/cores/teensy
+endif
+
+LIB_CXXSRC = WMath.cpp WString.cpp Print.cpp SPI.cpp
+
+ifeq ($(NEOPIXEL), 1)
+ LIB_CXXSRC += Adafruit_NeoPixel.cpp
+endif
+
+ifeq ($(LIQUID_TWI2), 0)
+ LIB_CXXSRC += LiquidCrystal.cpp
+else
+ LIB_SRC += twi.c
+ LIB_CXXSRC += Wire.cpp LiquidTWI2.cpp
+endif
+
+ifeq ($(WIRE), 1)
+ LIB_SRC += twi.c
+ LIB_CXXSRC += Wire.cpp
+endif
+
+ifeq ($(TONE), 1)
+ LIB_CXXSRC += Tone.cpp
+endif
+
+ifeq ($(U8GLIB), 1)
+ LIB_CXXSRC += U8glib.cpp
+ LIB_SRC += u8g_ll_api.c u8g_bitmap.c u8g_clip.c u8g_com_null.c u8g_delay.c \
+ u8g_page.c u8g_pb.c u8g_pb16h1.c u8g_rect.c u8g_state.c u8g_font.c \
+ u8g_font_6x13.c u8g_font_04b_03.c u8g_font_5x8.c
+endif
+
+ifeq ($(TMC), 1)
+ LIB_CXXSRC += TMCStepper.cpp COOLCONF.cpp DRV_STATUS.cpp IHOLD_IRUN.cpp \
+ CHOPCONF.cpp GCONF.cpp PWMCONF.cpp DRV_CONF.cpp DRVCONF.cpp DRVCTRL.cpp \
+ DRVSTATUS.cpp ENCMODE.cpp RAMP_STAT.cpp SGCSCONF.cpp SHORT_CONF.cpp \
+ SMARTEN.cpp SW_MODE.cpp SW_SPI.cpp TMC2130Stepper.cpp TMC2208Stepper.cpp \
+ TMC2209Stepper.cpp TMC2660Stepper.cpp TMC5130Stepper.cpp TMC5160Stepper.cpp
+endif
+
+ifeq ($(RELOC_WORKAROUND), 1)
+ LD_PREFIX=-nodefaultlibs
+ LD_SUFFIX=-lm -lgcc -lc -lgcc
+endif
+
+#Check for Arduino 1.0.0 or higher and use the correct source files for that version
+ifeq ($(shell [ $(ARDUINO_VERSION) -ge 100 ] && echo true), true)
+ LIB_CXXSRC += main.cpp
+else
+ LIB_SRC += pins_arduino.c main.c
+endif
+
+FORMAT = ihex
+
+# Name of this Makefile (used for "make depend").
+MAKEFILE = Makefile
+
+# Debugging format.
+# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2.
+# AVR (extended) COFF requires stabs, plus an avr-objcopy run.
+DEBUG = stabs
+
+OPT = s
+
+DEFINES ?=
+
+# Program settings
+CC = $(AVR_TOOLS_PATH)$(TOOL_PREFIX)-gcc
+CXX = $(AVR_TOOLS_PATH)$(TOOL_PREFIX)-g++
+OBJCOPY = $(AVR_TOOLS_PATH)$(TOOL_PREFIX)-objcopy
+OBJDUMP = $(AVR_TOOLS_PATH)$(TOOL_PREFIX)-objdump
+AR = $(AVR_TOOLS_PATH)$(TOOL_PREFIX)-ar
+SIZE = $(AVR_TOOLS_PATH)$(TOOL_PREFIX)-size
+NM = $(AVR_TOOLS_PATH)$(TOOL_PREFIX)-nm
+AVRDUDE = avrdude
+REMOVE = rm -f
+MV = mv -f
+
+# Place -D or -U options here
+CDEFS = -DF_CPU=$(F_CPU)UL ${addprefix -D , $(DEFINES)} -DARDUINO=$(ARDUINO_VERSION)
+CXXDEFS = $(CDEFS)
+
+ifeq ($(HARDWARE_VARIANT), Teensy)
+ CDEFS += -DUSB_SERIAL
+ LIB_SRC += usb.c pins_teensy.c
+ LIB_CXXSRC += usb_api.cpp
+
+else ifeq ($(HARDWARE_VARIANT), archim)
+ CDEFS += -DARDUINO_SAM_ARCHIM -DARDUINO_ARCH_SAM -D__SAM3X8E__
+ CDEFS += -DUSB_VID=0x27B1 -DUSB_PID=0x0001 -DUSBCON
+ CDEFS += '-DUSB_MANUFACTURER="UltiMachine"' '-DUSB_PRODUCT_STRING="Archim"'
+
+ LIB_CXXSRC += variant.cpp IPAddress.cpp Reset.cpp RingBuffer.cpp Stream.cpp \
+ UARTClass.cpp USARTClass.cpp abi.cpp new.cpp watchdog.cpp CDC.cpp \
+ PluggableUSB.cpp USBCore.cpp
+
+ LIB_SRC += cortex_handlers.c iar_calls_sam3.c syscalls_sam3.c dtostrf.c itoa.c
+
+ ifeq ($(U8GLIB), 1)
+ LIB_SRC += u8g_com_api.c u8g_pb32h1.c
+ endif
+endif
+
+# Add all the source directories as include directories too
+CINCS = ${addprefix -I ,${VPATH}}
+CXXINCS = ${addprefix -I ,${VPATH}}
+
+# Silence warnings for library code (won't work for .h files, unfortunately)
+LIBWARN = -w -Wno-packed-bitfield-compat
+
+# Compiler flag to set the C/CPP Standard level.
+CSTANDARD = -std=gnu99
+CXXSTANDARD = -std=gnu++11
+CDEBUG = -g$(DEBUG)
+CWARN = -Wall -Wstrict-prototypes -Wno-packed-bitfield-compat -Wno-pragmas -Wunused-parameter
+CXXWARN = -Wall -Wno-packed-bitfield-compat -Wno-pragmas -Wunused-parameter
+CTUNING = -fsigned-char -funsigned-bitfields -fno-exceptions \
+ -fshort-enums -ffunction-sections -fdata-sections
+ifneq ($(HARDWARE_MOTHERBOARD),)
+ CTUNING += -DMOTHERBOARD=${HARDWARE_MOTHERBOARD}
+endif
+
+#CEXTRA = -Wa,-adhlns=$(<:.c=.lst)
+CXXEXTRA = -fno-use-cxa-atexit -fno-threadsafe-statics -fno-rtti
+CFLAGS := $(CDEBUG) $(CDEFS) $(CINCS) -O$(OPT) $(CEXTRA) $(CTUNING) $(CSTANDARD)
+CXXFLAGS := $(CDEFS) $(CINCS) -O$(OPT) $(CXXEXTRA) $(CTUNING) $(CXXSTANDARD)
+ASFLAGS := $(CDEFS)
+#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs
+
+ifeq ($(HARDWARE_VARIANT), archim)
+ LD_PREFIX = -Wl,--gc-sections,-Map,Marlin.ino.map,--cref,--check-sections,--entry=Reset_Handler,--unresolved-symbols=report-all,--warn-common,--warn-section-align
+ LD_SUFFIX = $(LDLIBS)
+
+ LDFLAGS = -lm -T$(LDSCRIPT) -u _sbrk -u link -u _close -u _fstat -u _isatty
+ LDFLAGS += -u _lseek -u _read -u _write -u _exit -u kill -u _getpid
+else
+ LD_PREFIX = -Wl,--gc-sections,--relax
+ LDFLAGS = -lm
+ CTUNING += -flto
+endif
+
+# Programming support using avrdude. Settings and variables.
+AVRDUDE_PORT = $(UPLOAD_PORT)
+AVRDUDE_WRITE_FLASH = -Uflash:w:$(BUILD_DIR)/$(TARGET).hex:i
+ifeq ($(shell uname -s), Linux)
+ AVRDUDE_CONF = /etc/avrdude/avrdude.conf
+else
+ AVRDUDE_CONF = $(ARDUINO_INSTALL_DIR)/hardware/tools/avr/etc/avrdude.conf
+endif
+AVRDUDE_FLAGS = -D -C$(AVRDUDE_CONF) \
+ -p$(PROG_MCU) -P$(AVRDUDE_PORT) -c$(AVRDUDE_PROGRAMMER) \
+ -b$(UPLOAD_RATE)
+
+# Since Marlin 2.0, the source files may be distributed into several
+# different directories, so it is necessary to find them recursively
+
+SRC = $(shell find src -name '*.c' -type f)
+CXXSRC = $(shell find src -name '*.cpp' -type f)
+
+# Define all object files.
+OBJ = ${patsubst %.c, $(BUILD_DIR)/arduino/%.o, ${LIB_SRC}}
+OBJ += ${patsubst %.cpp, $(BUILD_DIR)/arduino/%.o, ${LIB_CXXSRC}}
+OBJ += ${patsubst %.S, $(BUILD_DIR)/arduino/%.o, ${LIB_ASRC}}
+OBJ += ${patsubst %.c, $(BUILD_DIR)/%.o, ${SRC}}
+OBJ += ${patsubst %.cpp, $(BUILD_DIR)/%.o, ${CXXSRC}}
+
+# Define all listing files.
+LST = $(LIB_ASRC:.S=.lst) $(LIB_CXXSRC:.cpp=.lst) $(LIB_SRC:.c=.lst)
+
+# Combine all necessary flags and optional flags.
+# Add target processor to flags.
+ALL_CFLAGS = $(MCU_FLAGS) $(CPU_FLAGS) $(CFLAGS) -I.
+ALL_CXXFLAGS = $(MCU_FLAGS) $(CPU_FLAGS) $(CXXFLAGS)
+ALL_ASFLAGS = $(MCU_FLAGS) $(CPU_FLAGS) $(ASFLAGS) -x assembler-with-cpp
+
+# set V=1 (eg, "make V=1") to print the full commands etc.
+ifneq ($V,1)
+ Pecho=@echo
+ P=@
+else
+ Pecho=@:
+ P=
+endif
+
+# Create required build hierarchy if it does not exist
+
+$(shell mkdir -p $(dir $(OBJ)))
+
+# Default target.
+all: sizeafter
+
+build: elf hex bin
+
+elf: $(BUILD_DIR)/$(TARGET).elf
+bin: $(BUILD_DIR)/$(TARGET).bin
+hex: $(BUILD_DIR)/$(TARGET).hex
+eep: $(BUILD_DIR)/$(TARGET).eep
+lss: $(BUILD_DIR)/$(TARGET).lss
+sym: $(BUILD_DIR)/$(TARGET).sym
+
+# Program the device.
+# Do not try to reset an Arduino if it's not one
+upload: $(BUILD_DIR)/$(TARGET).hex
+ifeq (${AVRDUDE_PROGRAMMER}, arduino)
+ stty hup < $(UPLOAD_PORT); true
+endif
+ $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH)
+ifeq (${AVRDUDE_PROGRAMMER}, arduino)
+ stty -hup < $(UPLOAD_PORT); true
+endif
+
+# Display size of file.
+HEXSIZE = $(SIZE) --target=$(FORMAT) $(BUILD_DIR)/$(TARGET).hex
+ELFSIZE = $(SIZE) $(SIZE_FLAGS) $(BUILD_DIR)/$(TARGET).elf; \
+ $(SIZE) $(BUILD_DIR)/$(TARGET).elf
+sizebefore:
+ $P if [ -f $(BUILD_DIR)/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(HEXSIZE); echo; fi
+
+sizeafter: build
+ $P if [ -f $(BUILD_DIR)/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); echo; fi
+
+
+# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
+COFFCONVERT=$(OBJCOPY) --debugging \
+ --change-section-address .data-0x800000 \
+ --change-section-address .bss-0x800000 \
+ --change-section-address .noinit-0x800000 \
+ --change-section-address .eeprom-0x810000
+
+
+coff: $(BUILD_DIR)/$(TARGET).elf
+ $(COFFCONVERT) -O coff-avr $(BUILD_DIR)/$(TARGET).elf $(TARGET).cof
+
+
+extcoff: $(TARGET).elf
+ $(COFFCONVERT) -O coff-ext-avr $(BUILD_DIR)/$(TARGET).elf $(TARGET).cof
+
+
+.SUFFIXES: .elf .hex .eep .lss .sym .bin
+.PRECIOUS: .o
+
+.elf.hex:
+ $(Pecho) " COPY $@"
+ $P $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
+
+.elf.bin:
+ $(Pecho) " COPY $@"
+ $P $(OBJCOPY) -O binary -R .eeprom $< $@
+
+.elf.eep:
+ -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
+ --change-section-lma .eeprom=0 -O $(FORMAT) $< $@
+
+# Create extended listing file from ELF output file.
+.elf.lss:
+ $(OBJDUMP) -h -S $< > $@
+
+# Create a symbol table from ELF output file.
+.elf.sym:
+ $(NM) -n $< > $@
+
+# Link: create ELF output file from library.
+
+$(BUILD_DIR)/$(TARGET).elf: $(OBJ) Configuration.h
+ $(Pecho) " CXX $@"
+ $P $(CXX) $(LD_PREFIX) $(ALL_CXXFLAGS) -o $@ -L. $(OBJ) $(LDFLAGS) $(LD_SUFFIX)
+
+# Object files that were found in "src" will be stored in $(BUILD_DIR)
+# in directories that mirror the structure of "src"
+
+$(BUILD_DIR)/%.o: %.c Configuration.h Configuration_adv.h $(MAKEFILE)
+ $(Pecho) " CC $<"
+ $P $(CC) -MMD -c $(ALL_CFLAGS) $(CWARN) $< -o $@
+
+$(BUILD_DIR)/%.o: %.cpp Configuration.h Configuration_adv.h $(MAKEFILE)
+ $(Pecho) " CXX $<"
+ $P $(CXX) -MMD -c $(ALL_CXXFLAGS) $(CXXWARN) $< -o $@
+
+# Object files for Arduino libs will be created in $(BUILD_DIR)/arduino
+
+$(BUILD_DIR)/arduino/%.o: %.c Configuration.h Configuration_adv.h $(MAKEFILE)
+ $(Pecho) " CC $<"
+ $P $(CC) -MMD -c $(ALL_CFLAGS) $(LIBWARN) $< -o $@
+
+$(BUILD_DIR)/arduino/%.o: %.cpp Configuration.h Configuration_adv.h $(MAKEFILE)
+ $(Pecho) " CXX $<"
+ $P $(CXX) -MMD -c $(ALL_CXXFLAGS) $(LIBWARN) $< -o $@
+
+$(BUILD_DIR)/arduino/%.o: %.S $(MAKEFILE)
+ $(Pecho) " CXX $<"
+ $P $(CXX) -MMD -c $(ALL_ASFLAGS) $< -o $@
+
+# Target: clean project.
+clean:
+ $(Pecho) " RMDIR $(BUILD_DIR)/"
+ $P rm -rf $(BUILD_DIR)
+
+
+.PHONY: all build elf hex eep lss sym program coff extcoff clean depend sizebefore sizeafter
+
+# Automatically include the dependency files created by gcc
+-include ${patsubst %.o, %.d, ${OBJ}}
diff --git a/Marlin/Marlin.ino b/Marlin/Marlin.ino
new file mode 100644
index 0000000000..57c825445f
--- /dev/null
+++ b/Marlin/Marlin.ino
@@ -0,0 +1,57 @@
+/*==============================================================================
+
+ Marlin Firmware
+
+ (c) 2011-2020 MarlinFirmware
+ Portions of Marlin are (c) by their respective authors.
+ All code complies with GPLv2 and/or GPLv3
+
+================================================================================
+
+Greetings! Thank you for choosing Marlin 2 as your 3D printer firmware.
+
+To configure Marlin you must edit Configuration.h and Configuration_adv.h
+located in the root 'Marlin' folder. Check our Configurations repository to
+see if there's a more suitable starting-point for your specific hardware.
+
+Before diving in, we recommend the following essential links:
+
+Marlin Firmware Official Website
+
+ - https://marlinfw.org/
+ The official Marlin Firmware website contains the most up-to-date
+ documentation. Contributions are always welcome!
+
+Configuration
+
+ - https://github.com/MarlinFirmware/Configurations
+ Example configurations for several printer models.
+
+ - https://www.youtube.com/watch?v=3gwWVFtdg-4
+ A good 20-minute overview of Marlin configuration by Tom Sanladerer.
+ (Applies to Marlin 1.0.x, so Jerk and Acceleration should be halved.)
+ Also... https://www.google.com/search?tbs=vid%3A1&q=configure+marlin
+
+ - https://marlinfw.org/docs/configuration/configuration.html
+ Marlin's configuration options are explained in more detail here.
+
+Getting Help
+
+ - https://reprap.org/forum/list.php?415
+ The Marlin Discussion Forum is a great place to get help from other Marlin
+ users who may have experienced similar issues to your own.
+
+ - https://github.com/MarlinFirmware/Marlin/issues
+ With a free GitHub account you can provide us with feedback, bug reports,
+ and feature requests via the Marlin Issue Queue.
+
+Contributing
+
+ - https://marlinfw.org/docs/development/contributing.html
+ If you'd like to contribute to Marlin, read this first!
+
+ - https://marlinfw.org/docs/development/coding_standards.html
+ Before submitting code get to know the Coding Standards.
+
+
+------------------------------------------------------------------------------*/
diff --git a/Marlin/Version.h b/Marlin/Version.h
new file mode 100644
index 0000000000..7aa526bfd5
--- /dev/null
+++ b/Marlin/Version.h
@@ -0,0 +1,79 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+////////////////////////////
+// VENDOR VERSION EXAMPLE //
+////////////////////////////
+
+/**
+ * Marlin release version identifier
+ */
+#define SHORT_BUILD_VERSION "2.1.3 MRiscoC"
+
+/**
+ * Verbose version identifier which should contain a reference to the location
+ * from where the binary was downloaded or the source code was compiled.
+ */
+#define DETAILED_BUILD_VERSION SHORT_BUILD_VERSION " Ender3V2-422-MM, based on bugfix-2.1.x"
+
+/**
+ * The STRING_DISTRIBUTION_DATE represents when the binary file was built,
+ * here we define this default string as the date where the latest release
+ * version was tagged.
+ */
+//#define STRING_DISTRIBUTION_DATE "2023-03-07"
+
+#define STRING_DISTRIBUTION_DATE __DATE__
+#define STRING_DISTRIBUTION_TIME __TIME__
+
+/**
+ * Defines a generic printer name to be output to the LCD after booting Marlin.
+ */
+#define MACHINE_NAME "Ender 3V2"
+
+/**
+ * The SOURCE_CODE_URL is the location where users will find the Marlin Source
+ * Code which is installed on the device. In most cases —unless the manufacturer
+ * has a distinct Github fork— the Source Code URL should just be the main
+ * Marlin repository.
+ */
+#define SOURCE_CODE_URL "github.com/mriscoc/Ender3V2S1"
+
+/**
+ * Default generic printer UUID.
+ */
+//#define DEFAULT_MACHINE_UUID "cede2a2f-41a2-4748-9b12-c55c62f367ff"
+
+/**
+ * The WEBSITE_URL is the location where users can get more information such as
+ * documentation about a specific Marlin release.
+ */
+#define WEBSITE_URL "github.com/mriscoc/Ender3V2S1/wiki"
+
+/**
+ * Set the vendor info the serial USB interface, if changable
+ * Currently only supported by DUE platform
+ */
+//#define USB_DEVICE_VENDOR_ID 0x0000
+//#define USB_DEVICE_PRODUCT_ID 0x0000
+//#define USB_DEVICE_MANUFACTURE_NAME WEBSITE_URL
diff --git a/Marlin/config.ini b/Marlin/config.ini
new file mode 100644
index 0000000000..0fb9fb0c93
--- /dev/null
+++ b/Marlin/config.ini
@@ -0,0 +1,211 @@
+#
+# Marlin Firmware
+# config.ini - Options to apply before the build
+#
+[config:base]
+ini_use_config = none
+
+# Load all config: sections in this file
+;ini_use_config = all
+# Load config file relative to Marlin/
+;ini_use_config = another.ini
+# Download configurations from GitHub
+;ini_use_config = example/Creality/Ender-5 Plus @ bugfix-2.1.x
+# Download configurations from your server
+;ini_use_config = https://me.myserver.com/path/to/configs
+# Evaluate config:base and do a config dump
+;ini_use_config = base
+;config_export = 2
+
+[config:minimal]
+motherboard = BOARD_RAMPS_14_EFB
+serial_port = 0
+baudrate = 250000
+
+use_watchdog = on
+thermal_protection_hotends = on
+thermal_protection_hysteresis = 4
+thermal_protection_period = 40
+
+bufsize = 4
+block_buffer_size = 16
+max_cmd_size = 96
+
+extruders = 1
+temp_sensor_0 = 1
+
+temp_hysteresis = 3
+heater_0_mintemp = 5
+heater_0_maxtemp = 275
+preheat_1_temp_hotend = 180
+
+bang_max = 255
+pidtemp = on
+pid_k1 = 0.95
+pid_max = BANG_MAX
+pid_functional_range = 10
+
+default_kp = 22.20
+default_ki = 1.08
+default_kd = 114.00
+
+x_driver_type = A4988
+y_driver_type = A4988
+z_driver_type = A4988
+e0_driver_type = A4988
+
+x_bed_size = 200
+x_min_pos = 0
+x_max_pos = X_BED_SIZE
+
+y_bed_size = 200
+y_min_pos = 0
+y_max_pos = Y_BED_SIZE
+
+z_min_pos = 0
+z_max_pos = 200
+
+x_home_dir = -1
+y_home_dir = -1
+z_home_dir = -1
+
+use_xmin_plug = on
+use_ymin_plug = on
+use_zmin_plug = on
+
+x_min_endstop_inverting = false
+y_min_endstop_inverting = false
+z_min_endstop_inverting = false
+
+default_axis_steps_per_unit = { 80, 80, 400, 500 }
+axis_relative_modes = { false, false, false, false }
+default_max_feedrate = { 300, 300, 5, 25 }
+default_max_acceleration = { 3000, 3000, 100, 10000 }
+
+homing_feedrate_mm_m = { (50*60), (50*60), (4*60) }
+homing_bump_divisor = { 2, 2, 4 }
+
+x_enable_on = 0
+y_enable_on = 0
+z_enable_on = 0
+e_enable_on = 0
+
+invert_x_dir = false
+invert_y_dir = true
+invert_z_dir = false
+invert_e0_dir = false
+
+invert_e_step_pin = false
+invert_x_step_pin = false
+invert_y_step_pin = false
+invert_z_step_pin = false
+
+disable_x = false
+disable_y = false
+disable_z = false
+disable_e = false
+
+proportional_font_ratio = 1.0
+default_nominal_filament_dia = 1.75
+
+junction_deviation_mm = 0.013
+
+default_acceleration = 3000
+default_travel_acceleration = 3000
+default_retract_acceleration = 3000
+
+default_minimumfeedrate = 0.0
+default_mintravelfeedrate = 0.0
+
+minimum_planner_speed = 0.05
+min_steps_per_segment = 6
+default_minsegmenttime = 20000
+
+[config:basic]
+bed_overshoot = 10
+busy_while_heating = on
+default_ejerk = 5.0
+default_keepalive_interval = 2
+default_leveling_fade_height = 0.0
+disable_inactive_extruder = on
+display_charset_hd44780 = JAPANESE
+eeprom_boot_silent = on
+eeprom_chitchat = on
+endstoppullups = on
+extrude_maxlength = 200
+extrude_mintemp = 170
+host_keepalive_feature = on
+hotend_overshoot = 15
+jd_handle_small_segments = on
+lcd_info_screen_style = 0
+lcd_language = en
+max_bed_power = 255
+mesh_inset = 0
+min_software_endstops = on
+max_software_endstops = on
+min_software_endstop_x = on
+min_software_endstop_y = on
+min_software_endstop_z = on
+max_software_endstop_x = on
+max_software_endstop_y = on
+max_software_endstop_z = on
+preheat_1_fan_speed = 0
+preheat_1_label = "PLA"
+preheat_1_temp_bed = 70
+prevent_cold_extrusion = on
+prevent_lengthy_extrude = on
+printjob_timer_autostart = on
+probing_margin = 10
+show_bootscreen = on
+soft_pwm_scale = 0
+string_config_h_author = "(none, default config)"
+temp_bed_hysteresis = 3
+temp_bed_residency_time = 10
+temp_bed_window = 1
+temp_residency_time = 10
+temp_window = 1
+validate_homing_endstops = on
+xy_probe_feedrate = (133*60)
+z_clearance_between_probes = 5
+z_clearance_deploy_probe = 10
+z_clearance_multi_probe = 5
+
+[config:advanced]
+arc_support = on
+auto_report_temperatures = on
+autotemp = on
+autotemp_oldweight = 0.98
+bed_check_interval = 5000
+default_stepper_deactive_time = 120
+default_volumetric_extruder_limit = 0.00
+disable_inactive_e = true
+disable_inactive_x = true
+disable_inactive_y = true
+disable_inactive_z = true
+e0_auto_fan_pin = -1
+encoder_100x_steps_per_sec = 80
+encoder_10x_steps_per_sec = 30
+encoder_rate_multiplier = on
+extended_capabilities_report = on
+extruder_auto_fan_speed = 255
+extruder_auto_fan_temperature = 50
+fanmux0_pin = -1
+fanmux1_pin = -1
+fanmux2_pin = -1
+faster_gcode_parser = on
+homing_bump_mm = { 5, 5, 2 }
+max_arc_segment_mm = 1.0
+min_arc_segment_mm = 0.1
+min_circle_segments = 72
+n_arc_correction = 25
+serial_overrun_protection = on
+slowdown = on
+slowdown_divisor = 2
+temp_sensor_bed = 0
+thermal_protection_bed_hysteresis = 2
+thermocouple_max_errors = 15
+tx_buffer_size = 0
+watch_bed_temp_increase = 2
+watch_bed_temp_period = 60
+watch_temp_increase = 2
+watch_temp_period = 20
diff --git a/Marlin/lib/proui/gd32f10/libproui_abl.a b/Marlin/lib/proui/gd32f10/libproui_abl.a
index b3a425249b..e12220123f 100644
Binary files a/Marlin/lib/proui/gd32f10/libproui_abl.a and b/Marlin/lib/proui/gd32f10/libproui_abl.a differ
diff --git a/Marlin/lib/proui/gd32f10/libproui_mbl.a b/Marlin/lib/proui/gd32f10/libproui_mbl.a
index b3a425249b..e15a76735b 100644
Binary files a/Marlin/lib/proui/gd32f10/libproui_mbl.a and b/Marlin/lib/proui/gd32f10/libproui_mbl.a differ
diff --git a/Marlin/lib/proui/gd32f10/libproui_ubl.a b/Marlin/lib/proui/gd32f10/libproui_ubl.a
index b3a425249b..a3d81c241f 100644
Binary files a/Marlin/lib/proui/gd32f10/libproui_ubl.a and b/Marlin/lib/proui/gd32f10/libproui_ubl.a differ
diff --git a/Marlin/lib/proui/stm32f1/libproui_abl.a b/Marlin/lib/proui/stm32f1/libproui_abl.a
index b3a425249b..6207ce8551 100644
Binary files a/Marlin/lib/proui/stm32f1/libproui_abl.a and b/Marlin/lib/proui/stm32f1/libproui_abl.a differ
diff --git a/Marlin/lib/proui/stm32f1/libproui_mbl.a b/Marlin/lib/proui/stm32f1/libproui_mbl.a
index b3a425249b..067b857603 100644
Binary files a/Marlin/lib/proui/stm32f1/libproui_mbl.a and b/Marlin/lib/proui/stm32f1/libproui_mbl.a differ
diff --git a/Marlin/lib/proui/stm32f1/libproui_ubl.a b/Marlin/lib/proui/stm32f1/libproui_ubl.a
index b3a425249b..be8d37e733 100644
Binary files a/Marlin/lib/proui/stm32f1/libproui_ubl.a and b/Marlin/lib/proui/stm32f1/libproui_ubl.a differ
diff --git a/Marlin/lib/proui/stm32f4/libproui_abl.a b/Marlin/lib/proui/stm32f4/libproui_abl.a
index b3a425249b..a5057f86d3 100644
Binary files a/Marlin/lib/proui/stm32f4/libproui_abl.a and b/Marlin/lib/proui/stm32f4/libproui_abl.a differ
diff --git a/Marlin/lib/proui/stm32f4/libproui_mbl.a b/Marlin/lib/proui/stm32f4/libproui_mbl.a
index b3a425249b..d66e093b3f 100644
Binary files a/Marlin/lib/proui/stm32f4/libproui_mbl.a and b/Marlin/lib/proui/stm32f4/libproui_mbl.a differ
diff --git a/Marlin/lib/proui/stm32f4/libproui_ubl.a b/Marlin/lib/proui/stm32f4/libproui_ubl.a
index b3a425249b..66db6a12c9 100644
Binary files a/Marlin/lib/proui/stm32f4/libproui_ubl.a and b/Marlin/lib/proui/stm32f4/libproui_ubl.a differ
diff --git a/Marlin/lib/proui/stm32g0/libproui_abl.a b/Marlin/lib/proui/stm32g0/libproui_abl.a
index b3a425249b..8984b372e8 100644
Binary files a/Marlin/lib/proui/stm32g0/libproui_abl.a and b/Marlin/lib/proui/stm32g0/libproui_abl.a differ
diff --git a/Marlin/lib/proui/stm32g0/libproui_mbl.a b/Marlin/lib/proui/stm32g0/libproui_mbl.a
index b3a425249b..78832ef727 100644
Binary files a/Marlin/lib/proui/stm32g0/libproui_mbl.a and b/Marlin/lib/proui/stm32g0/libproui_mbl.a differ
diff --git a/Marlin/lib/proui/stm32g0/libproui_ubl.a b/Marlin/lib/proui/stm32g0/libproui_ubl.a
index b3a425249b..c3b56b3c68 100644
Binary files a/Marlin/lib/proui/stm32g0/libproui_ubl.a and b/Marlin/lib/proui/stm32g0/libproui_ubl.a differ
diff --git a/Marlin/lib/readme.txt b/Marlin/lib/readme.txt
new file mode 100644
index 0000000000..5ec60aa858
--- /dev/null
+++ b/Marlin/lib/readme.txt
@@ -0,0 +1,36 @@
+
+This directory is intended for the project specific (private) libraries.
+PlatformIO will compile them to static libraries and link to executable file.
+
+The source code of each library should be placed in separate directory, like
+"lib/private_lib/[here are source files]".
+
+For example, see how can be organized `Foo` and `Bar` libraries:
+
+|--lib
+| |--Bar
+| | |--docs
+| | |--examples
+| | |--src
+| | |- Bar.c
+| | |- Bar.h
+| |--Foo
+| | |- Foo.c
+| | |- Foo.h
+| |- readme.txt --> THIS FILE
+|- platformio.ini
+|--src
+ |- main.c
+
+Then in `src/main.c` you should use:
+
+#include
+#include
+
+// rest H/C/CPP code
+
+PlatformIO will find your libraries automatically, configure preprocessor's
+include paths and build them.
+
+More information about PlatformIO Library Dependency Finder
+- https://docs.platformio.org/page/librarymanager/ldf.html
diff --git a/Marlin/src/HAL/HAL.h b/Marlin/src/HAL/HAL.h
new file mode 100644
index 0000000000..5186578019
--- /dev/null
+++ b/Marlin/src/HAL/HAL.h
@@ -0,0 +1,47 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include "platforms.h"
+
+#ifndef GCC_VERSION
+ #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#endif
+
+#include HAL_PATH(.,HAL.h)
+extern MarlinHAL hal;
+
+#define HAL_ADC_RANGE _BV(HAL_ADC_RESOLUTION)
+
+#ifndef I2C_ADDRESS
+ #define I2C_ADDRESS(A) uint8_t(A)
+#endif
+
+// Needed for AVR sprintf_P PROGMEM extension
+#ifndef S_FMT
+ #define S_FMT "%s"
+#endif
+
+// String helper
+#ifndef PGMSTR
+ #define PGMSTR(NAM,STR) const char NAM[] = STR
+#endif
diff --git a/Marlin/src/HAL/SAMD21/HAL.cpp b/Marlin/src/HAL/SAMD21/HAL.cpp
new file mode 100644
index 0000000000..14c439eeb9
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/HAL.cpp
@@ -0,0 +1,212 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+#ifdef __SAMD21__
+
+#include "../../inc/MarlinConfig.h"
+
+#include
+
+#if USING_HW_SERIALUSB
+ DefaultSerial1 MSerialUSB(false, SerialUSB);
+#endif
+#if USING_HW_SERIAL0
+ DefaultSerial2 MSerial1(false, Serial1);
+#endif
+#if USING_HW_SERIAL1
+ DefaultSerial3 MSerial2(false, Serial2);
+#endif
+
+
+
+#define WDT_CONFIG_PER_7_Val 0x9u
+#define WDT_CONFIG_PER_Pos 0
+#define WDT_CONFIG_PER_7 (WDT_CONFIG_PER_7_Val << WDT_CONFIG_PER_Pos)
+
+#if ENABLED(USE_WATCHDOG)
+
+ #define WDT_TIMEOUT_REG TERN(WATCHDOG_DURATION_8S, WDT_CONFIG_PER_CYC8192, WDT_CONFIG_PER_CYC4096) // 4 or 8 second timeout
+
+ void MarlinHAL::watchdog_init() {
+ // Set up the generic clock (GCLK2) used to clock the watchdog timer at 1.024kHz
+ GCLK->GENDIV.reg = GCLK_GENDIV_DIV(4) | // Divide the 32.768kHz clock source by divisor 32, where 2^(4 + 1): 32.768kHz/32=1.024kHz
+ GCLK_GENDIV_ID(2); // Select Generic Clock (GCLK) 2
+ while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
+
+ REG_GCLK_GENCTRL = GCLK_GENCTRL_DIVSEL | // Set to divide by 2^(GCLK_GENDIV_DIV(4) + 1)
+ GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
+ GCLK_GENCTRL_GENEN | // Enable GCLK2
+ GCLK_GENCTRL_SRC_OSCULP32K | // Set the clock source to the ultra low power oscillator (OSCULP32K)
+ GCLK_GENCTRL_ID(2); // Select GCLK2
+ while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
+
+ // Feed GCLK2 to WDT (Watchdog Timer)
+ REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK2 to the WDT
+ GCLK_CLKCTRL_GEN_GCLK2 | // Select GCLK2
+ GCLK_CLKCTRL_ID_WDT; // Feed the GCLK2 to the WDT
+ while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
+
+ WDT->CONFIG.bit.PER = WDT_CONFIG_PER_7; // Set the WDT reset timeout to 4 seconds
+ while (WDT->STATUS.bit.SYNCBUSY); // Wait for synchronization
+ REG_WDT_CTRL = WDT_CTRL_ENABLE; // Enable the WDT in normal mode
+ while (WDT->STATUS.bit.SYNCBUSY); // Wait for synchronization
+ }
+
+ // Reset watchdog. MUST be called at least every 4 seconds after the
+ // first watchdog_init or SAMD will go into emergency procedures.
+ void MarlinHAL::watchdog_refresh() {
+ WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY;
+ while (WDT->STATUS.bit.SYNCBUSY);
+ }
+
+#endif
+
+// ------------------------
+// Types
+// ------------------------
+
+// ------------------------
+// Private Variables
+// ------------------------
+
+// ------------------------
+// Private functions
+// ------------------------
+
+void MarlinHAL::dma_init() {}
+
+// ------------------------
+// Public functions
+// ------------------------
+
+// HAL initialization task
+void MarlinHAL::init() {
+ TERN_(DMA_IS_REQUIRED, dma_init());
+ #if ENABLED(SDSUPPORT)
+ #if HAS_SD_DETECT && SD_CONNECTION_IS(ONBOARD)
+ SET_INPUT_PULLUP(SD_DETECT_PIN);
+ #endif
+ OUT_WRITE(SDSS, HIGH); // Try to set SDSS inactive before any other SPI users start up
+ #endif
+}
+
+#pragma push_macro("WDT")
+#undef WDT // Required to be able to use '.bit.WDT'. Compiler wrongly replace struct field with WDT define
+uint8_t MarlinHAL::get_reset_source() {
+
+ return 0;
+}
+#pragma pop_macro("WDT")
+
+void MarlinHAL::reboot() { NVIC_SystemReset(); }
+
+extern "C" {
+ void * _sbrk(int incr);
+ extern unsigned int __bss_end__; // end of bss section
+}
+
+// Return free memory between end of heap (or end bss) and whatever is current
+int freeMemory() {
+ int free_memory, heap_end = (int)_sbrk(0);
+ return (int)&free_memory - (heap_end ?: (int)&__bss_end__);
+}
+
+// ------------------------
+// ADC
+// ------------------------
+
+uint16_t MarlinHAL::adc_result;
+
+void MarlinHAL::adc_init() {
+ /* thanks to https://www.eevblog.com/forum/microcontrollers/samd21g18-adc-with-resrdy-interrupts-only-reads-once-or-twice/ */
+
+ ADC->CTRLA.bit.ENABLE = false;
+ while(ADC->STATUS.bit.SYNCBUSY);
+
+ // load chip corrections
+ uint32_t bias = (*((uint32_t *) ADC_FUSES_BIASCAL_ADDR) & ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos;
+ uint32_t linearity = (*((uint32_t *) ADC_FUSES_LINEARITY_0_ADDR) & ADC_FUSES_LINEARITY_0_Msk) >> ADC_FUSES_LINEARITY_0_Pos;
+ linearity |= ((*((uint32_t *) ADC_FUSES_LINEARITY_1_ADDR) & ADC_FUSES_LINEARITY_1_Msk) >> ADC_FUSES_LINEARITY_1_Pos) << 5;
+
+ /* Wait for bus synchronization. */
+ while (ADC->STATUS.bit.SYNCBUSY) {};
+
+ ADC->CALIB.reg = ADC_CALIB_BIAS_CAL(bias) | ADC_CALIB_LINEARITY_CAL(linearity);
+
+ /* Wait for bus synchronization. */
+ while (ADC->STATUS.bit.SYNCBUSY) {};
+
+ ADC->CTRLA.bit.SWRST = true;
+ while(ADC->STATUS.bit.SYNCBUSY);
+
+ ADC->REFCTRL.reg = ADC_REFCTRL_REFSEL_INTVCC1;
+ ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_32| ADC_AVGCTRL_ADJRES(4);;
+
+
+ ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV128 |
+ ADC_CTRLB_RESSEL_16BIT |
+ ADC_CTRLB_FREERUN;
+ while(ADC->STATUS.bit.SYNCBUSY);
+
+ ADC->SAMPCTRL.bit.SAMPLEN = 0x00;
+ while(ADC->STATUS.bit.SYNCBUSY);
+
+ ADC->INPUTCTRL.reg = ADC_INPUTCTRL_INPUTSCAN(HAL_ADC_AIN_LEN) // scan (INPUTSCAN + NUM_EXTUDERS - 1) pins
+ | ADC_INPUTCTRL_GAIN_DIV2 |ADC_INPUTCTRL_MUXNEG_GND| HAL_ADC_AIN_START ; /* set to first AIN */
+
+ while(ADC->STATUS.bit.SYNCBUSY);
+
+ ADC->INTENSET.reg |= ADC_INTENSET_RESRDY; // enable Result Ready ADC interrupts
+ while (ADC->STATUS.bit.SYNCBUSY);
+
+ NVIC_EnableIRQ(ADC_IRQn); // enable ADC interrupts
+
+ NVIC_SetPriority(ADC_IRQn, 3);
+
+ ADC->CTRLA.bit.ENABLE = true;
+}
+
+volatile uint32_t adc_results[HAL_ADC_AIN_NUM_SENSORS];
+
+void ADC_Handler() {
+ while(ADC->STATUS.bit.SYNCBUSY == 1);
+ int pos = ADC->INPUTCTRL.bit.INPUTOFFSET;
+
+ adc_results[pos] = ADC->RESULT.reg; /* Read the value. */
+ ADC->INTFLAG.reg = ADC_INTENSET_RESRDY; /* Clear the data ready flag. */
+}
+
+void MarlinHAL::adc_start(const pin_t pin) {
+ /* due to the way INPUTOFFSET works, the last sensor is the first position in the array
+ and we want the ADC_handler interrupt to be as simple possible, so we do the calculation here.
+ */
+ unsigned int pos = PIN_TO_INPUTCTRL(pin) - HAL_ADC_AIN_START + 1;
+ if (pos == HAL_ADC_AIN_NUM_SENSORS) pos = 0;
+ adc_result = adc_results[pos]; // 16-bit resolution
+ //adc_result = 0xFFFF;
+}
+
+#endif // __SAMD21__
diff --git a/Marlin/src/HAL/SAMD21/HAL.h b/Marlin/src/HAL/SAMD21/HAL.h
new file mode 100644
index 0000000000..1854e523ed
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/HAL.h
@@ -0,0 +1,223 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+
+#define CPU_32_BIT
+
+#include "../shared/Marduino.h"
+#include "../shared/math_32bit.h"
+#include "../shared/HAL_SPI.h"
+#include "fastio.h"
+
+// ------------------------
+// Serial ports
+// ------------------------
+#include "../../core/serial_hook.h"
+typedef ForwardSerial1Class< decltype(SerialUSB) > DefaultSerial1;
+extern DefaultSerial1 MSerialUSB;
+
+// Serial ports
+typedef ForwardSerial1Class< decltype(Serial1) > DefaultSerial2;
+typedef ForwardSerial1Class< decltype(Serial2) > DefaultSerial3;
+
+extern DefaultSerial2 MSerial0;
+extern DefaultSerial3 MSerial1;
+
+
+#define __MSERIAL(X) MSerial##X
+#define _MSERIAL(X) __MSERIAL(X)
+#define MSERIAL(X) _MSERIAL(INCREMENT(X))
+
+#if WITHIN(SERIAL_PORT, 0, 1)
+ #define MYSERIAL1 MSERIAL(SERIAL_PORT)
+#elif SERIAL_PORT == -1
+ #define MYSERIAL1 MSerialUSB
+#else
+ #error "SERIAL_PORT must be -1 (Native USB only)."
+#endif
+
+#ifdef SERIAL_PORT_2
+ #if WITHIN(SERIAL_PORT_2, 0, 1)
+ #define MYSERIAL2 MSERIAL(SERIAL_PORT)
+ #elif SERIAL_PORT_2 == -1
+ #define MYSERIAL2 MSerialUSB
+ #else
+ #error "SERIAL_PORT_2 must be -1 (Native USB only)."
+ #endif
+#endif
+
+#ifdef MMU2_SERIAL_PORT
+ #if WITHIN(MMU2_SERIAL_PORT, 0, 1)
+ #define MMU2_SERIAL MSERIAL(SERIAL_PORT)
+ #elif MMU2_SERIAL_PORT == -1
+ #define MMU2_SERIAL MSerialUSB
+ #else
+ #error "MMU2_SERIAL_PORT must be -1 (Native USB only)."
+ #endif
+#endif
+
+#ifdef LCD_SERIAL_PORT
+ #if WITHIN(LCD_SERIAL_PORT, 0, 1)
+ #define LCD_SERIAL MSERIAL(SERIAL_PORT)
+ #elif LCD_SERIAL_PORT == -1
+ #define LCD_SERIAL MSerialUSB
+ #else
+ #error "LCD_SERIAL_PORT must be -1 (Native USB only)."
+ #endif
+#endif
+
+typedef int8_t pin_t;
+
+#define SHARED_SERVOS HAS_SERVOS // Use shared/servos.cpp
+
+class Servo;
+typedef Servo hal_servo_t;
+
+//
+// Interrupts
+//
+#define CRITICAL_SECTION_START() const bool irqon = !__get_PRIMASK(); __disable_irq()
+#define CRITICAL_SECTION_END() if (irqon) __enable_irq()
+
+#define cli() __disable_irq() // Disable interrupts
+#define sei() __enable_irq() // Enable interrupts
+
+//
+// ADC
+//
+
+#define HAL_ADC_FILTERED 1 // Disable Marlin's oversampling. The HAL filters ADC values.
+#define HAL_ADC_VREF 3.3
+#define HAL_ADC_RESOLUTION 12
+#define HAL_ADC_AIN_START ADC_INPUTCTRL_MUXPOS_PIN3
+#define HAL_ADC_AIN_NUM_SENSORS 3
+#define HAL_ADC_AIN_LEN HAL_ADC_AIN_NUM_SENSORS-1
+
+//
+// Pin Mapping for M42, M43, M226
+//
+#define GET_PIN_MAP_PIN(index) index
+#define GET_PIN_MAP_INDEX(pin) pin
+#define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval)
+
+//
+// Tone
+//
+void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration=0);
+void noTone(const pin_t _pin);
+
+// ------------------------
+// Class Utilities
+// ------------------------
+
+#pragma GCC diagnostic push
+#if GCC_VERSION <= 50000
+ #pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+char *dtostrf(double __val, signed char __width, unsigned char __prec, char *__s);
+
+extern "C" int freeMemory();
+
+#ifdef __cplusplus
+ }
+#endif
+
+#pragma GCC diagnostic pop
+
+// ------------------------
+// MarlinHAL Class
+// ------------------------
+
+class MarlinHAL {
+public:
+
+ // Earliest possible init, before setup()
+ MarlinHAL() {}
+
+ // Watchdog
+ static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {});
+ static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {});
+
+ static void init(); // Called early in setup()
+ static void init_board() {} // Called less early in setup()
+ static void reboot(); // Restart the firmware from 0x0
+
+ // Interrupts
+ static bool isr_state() { return !__get_PRIMASK(); }
+ static void isr_on() { sei(); }
+ static void isr_off() { cli(); }
+
+ static void delay_ms(const int ms) { delay(ms); }
+
+ // Tasks, called from idle()
+ static void idletask() {}
+
+ // Reset
+ static uint8_t get_reset_source();
+ static void clear_reset_source() {}
+
+ // Free SRAM
+ static int freeMemory() { return ::freeMemory(); }
+
+ //
+ // ADC Methods
+ //
+
+ static uint16_t adc_result;
+
+ // Called by Temperature::init once at startup
+ static void adc_init();
+
+ // Called by Temperature::init for each sensor at startup
+ static void adc_enable(const uint8_t ch) {}
+
+ // Begin ADC sampling on the given pin. Called from Temperature::isr!
+ static void adc_start(const pin_t pin);
+
+ // Is the ADC ready for reading?
+ static bool adc_ready() { return true; }
+
+ // The current value of the ADC register
+ static uint16_t adc_value() { return adc_result; }
+
+ /**
+ * Set the PWM duty cycle for the pin to the given value.
+ * No option to invert the duty cycle [default = false]
+ * No option to change the scale of the provided value to enable finer PWM duty control [default = 255]
+ */
+ static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false) {
+ analogWrite(pin, v);
+ }
+
+private:
+ static void dma_init();
+};
diff --git a/Marlin/src/HAL/SAMD21/HAL_SPI.cpp b/Marlin/src/HAL/SAMD21/HAL_SPI.cpp
new file mode 100644
index 0000000000..0fc530cdb2
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/HAL_SPI.cpp
@@ -0,0 +1,148 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+
+/**
+ * Hardware and software SPI implementations are included in this file.
+ *
+ * Control of the slave select pin(s) is handled by the calling routines and
+ * SAMD21 let hardware SPI handling to remove SS from its logic.
+ */
+
+#ifdef __SAMD21__
+
+// --------------------------------------------------------------------------
+// Includes
+// --------------------------------------------------------------------------
+
+#include "../../inc/MarlinConfig.h"
+#include
+
+// --------------------------------------------------------------------------
+// Public functions
+// --------------------------------------------------------------------------
+
+#if EITHER(SOFTWARE_SPI, FORCE_SOFT_SPI)
+
+ // ------------------------
+ // Software SPI
+ // ------------------------
+ #error "Software SPI not supported for SAMD21. Use Hardware SPI."
+
+#else // !SOFTWARE_SPI
+
+ static SPISettings spiConfig;
+
+ // ------------------------
+ // Hardware SPI
+ // ------------------------
+ void spiBegin() {
+ spiInit(SPI_HALF_SPEED);
+ }
+
+ void spiInit(uint8_t spiRate) {
+ // Use datarates Marlin uses
+ uint32_t clock;
+ switch (spiRate) {
+ case SPI_FULL_SPEED: clock = 8000000; break;
+ case SPI_HALF_SPEED: clock = 4000000; break;
+ case SPI_QUARTER_SPEED: clock = 2000000; break;
+ case SPI_EIGHTH_SPEED: clock = 1000000; break;
+ case SPI_SIXTEENTH_SPEED: clock = 500000; break;
+ case SPI_SPEED_5: clock = 250000; break;
+ case SPI_SPEED_6: clock = 125000; break;
+ default: clock = 4000000; break; // Default from the SPI library
+ }
+ spiConfig = SPISettings(clock, MSBFIRST, SPI_MODE0);
+ SPI.begin();
+ }
+
+ /**
+ * @brief Receives a single byte from the SPI port.
+ *
+ * @return Byte received
+ *
+ * @details
+ */
+ uint8_t spiRec() {
+ SPI.beginTransaction(spiConfig);
+ uint8_t returnByte = SPI.transfer(0xFF);
+ SPI.endTransaction();
+ return returnByte;
+ }
+
+ /**
+ * @brief Receives a number of bytes from the SPI port to a buffer
+ *
+ * @param buf Pointer to starting address of buffer to write to.
+ * @param nbyte Number of bytes to receive.
+ * @return Nothing
+ */
+ void spiRead(uint8_t *buf, uint16_t nbyte) {
+ if (nbyte == 0) return;
+ memset(buf, 0xFF, nbyte);
+
+ SPI.beginTransaction(spiConfig);
+ SPI.transfer(buf, nbyte);
+ SPI.endTransaction();
+
+ }
+
+ /**
+ * @brief Sends a single byte on SPI port
+ *
+ * @param b Byte to send
+ *
+ * @details
+ */
+ void spiSend(uint8_t b) {
+ SPI.beginTransaction(spiConfig);
+ SPI.transfer(b);
+ SPI.endTransaction();
+ }
+
+ /**
+ * @brief Write token and then write from 512 byte buffer to SPI (for SD card)
+ *
+ * @param buf Pointer with buffer start address
+ * @return Nothing
+ *
+ * @details Uses DMA
+ */
+ void spiSendBlock(uint8_t token, const uint8_t *buf) {
+ SPI.beginTransaction(spiConfig);
+ SPI.transfer(token);
+ SPI.transfer((uint8_t*)buf, 512);
+ SPI.endTransaction();
+ }
+
+ void spiBeginTransaction(uint32_t spiClock, uint8_t bitOrder, uint8_t dataMode) {
+ spiConfig = SPISettings(spiClock, (BitOrder)bitOrder, dataMode);
+ SPI.beginTransaction(spiConfig);
+ }
+#endif // !SOFTWARE_SPI
+
+#endif // __SAMD21__
diff --git a/Marlin/src/HAL/SAMD21/MarlinSPI.h b/Marlin/src/HAL/SAMD21/MarlinSPI.h
new file mode 100644
index 0000000000..7b5392793e
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/MarlinSPI.h
@@ -0,0 +1,31 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+#pragma once
+
+#include
+
+using MarlinSPI = SPIClass;
diff --git a/Marlin/src/HAL/SAMD21/QSPIFlash.cpp b/Marlin/src/HAL/SAMD21/QSPIFlash.cpp
new file mode 100644
index 0000000000..fa54c62071
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/QSPIFlash.cpp
@@ -0,0 +1,82 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(QSPI_EEPROM)
+
+#include "QSPIFlash.h"
+
+#define INVALID_ADDR 0xFFFFFFFF
+#define SECTOR_OF(a) (a & ~(SFLASH_SECTOR_SIZE - 1))
+#define OFFSET_OF(a) (a & (SFLASH_SECTOR_SIZE - 1))
+
+Adafruit_SPIFlashBase * QSPIFlash::_flashBase = nullptr;
+uint8_t QSPIFlash::_buf[SFLASH_SECTOR_SIZE];
+uint32_t QSPIFlash::_addr = INVALID_ADDR;
+
+void QSPIFlash::begin() {
+ if (_flashBase) return;
+
+ _flashBase = new Adafruit_SPIFlashBase(new Adafruit_FlashTransport_QSPI());
+ _flashBase->begin(nullptr);
+}
+
+size_t QSPIFlash::size() {
+ return _flashBase->size();
+}
+
+uint8_t QSPIFlash::readByte(const uint32_t address) {
+ if (SECTOR_OF(address) == _addr) return _buf[OFFSET_OF(address)];
+
+ return _flashBase->read8(address);
+}
+
+void QSPIFlash::writeByte(const uint32_t address, const uint8_t value) {
+ uint32_t const sector_addr = SECTOR_OF(address);
+
+ // Page changes, flush old and update new cache
+ if (sector_addr != _addr) {
+ flush();
+ _addr = sector_addr;
+
+ // read a whole page from flash
+ _flashBase->readBuffer(sector_addr, _buf, SFLASH_SECTOR_SIZE);
+ }
+
+ _buf[OFFSET_OF(address)] = value;
+}
+
+void QSPIFlash::flush() {
+ if (_addr == INVALID_ADDR) return;
+
+ _flashBase->eraseSector(_addr / SFLASH_SECTOR_SIZE);
+ _flashBase->writeBuffer(_addr, _buf, SFLASH_SECTOR_SIZE);
+
+ _addr = INVALID_ADDR;
+}
+
+#endif // QSPI_EEPROM
diff --git a/Marlin/src/HAL/SAMD21/QSPIFlash.h b/Marlin/src/HAL/SAMD21/QSPIFlash.h
new file mode 100644
index 0000000000..58822fe05f
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/QSPIFlash.h
@@ -0,0 +1,49 @@
+/**
+ * @file QSPIFlash.h
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach and Dean Miller for Adafruit Industries LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Derived from Adafruit_SPIFlash class with no SdFat references
+ */
+#pragma once
+
+#include
+
+// This class extends Adafruit_SPIFlashBase by adding caching support.
+//
+// This class will use 4096 Bytes of RAM as a block cache.
+class QSPIFlash {
+ public:
+ static void begin();
+ static size_t size();
+ static uint8_t readByte(const uint32_t address);
+ static void writeByte(const uint32_t address, const uint8_t v);
+ static void flush();
+
+ private:
+ static Adafruit_SPIFlashBase * _flashBase;
+ static uint8_t _buf[SFLASH_SECTOR_SIZE];
+ static uint32_t _addr;
+};
+
+extern QSPIFlash qspi;
diff --git a/Marlin/src/HAL/SAMD21/SAMD21.h b/Marlin/src/HAL/SAMD21/SAMD21.h
new file mode 100644
index 0000000000..8e9d17fc50
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/SAMD21.h
@@ -0,0 +1,66 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+
+#define SYNC(sc) while (sc) { \
+ asm(""); \
+ }
+
+// Get SAMD port/pin from specified arduino pin
+#define GET_SAMD_PORT(P) _GET_SAMD_PORT(PIN_TO_SAMD_PIN(P))
+#define GET_SAMD_PIN(P) _GET_SAMD_PIN(PIN_TO_SAMD_PIN(P))
+
+// Get external interrupt line associated to specified arduino pin
+#define PIN_TO_EILINE(P) _SAMDPORTPIN_TO_EILINE(GET_SAMD_PORT(P), GET_SAMD_PIN(P))
+
+// Get adc/ain associated to specified arduino pin
+#define PIN_TO_ADC(P) (ANAPIN_TO_ADCAIN(P) >> 8)
+
+// Private defines
+#define PIN_TO_SAMD_PIN(P) DIO##P##_PIN
+
+#define _GET_SAMD_PORT(P) ((P) >> 5)
+#define _GET_SAMD_PIN(P) ((P) & 0x1F)
+
+// Get external interrupt line
+#define _SAMDPORTPIN_TO_EILINE(P,B) ((P == 0 && WITHIN(B, 0, 31) && B != 26 && B != 28 && B != 29) ? (B) & 0xF \
+ : (P == 1 && (WITHIN(B, 0, 25) || WITHIN(B, 30, 31))) ? (B) & 0xF \
+ : (P == 1 && WITHIN(B, 26, 29)) ? 12 + (B) - 26 \
+ : (P == 2 && (WITHIN(B, 0, 6) || WITHIN(B, 10, 31)) && B != 29) ? (B) & 0xF \
+ : (P == 2 && B == 7) ? 9 \
+ : (P == 3 && WITHIN(B, 0, 1)) ? (B) \
+ : (P == 3 && WITHIN(B, 8, 12)) ? 3 + (B) - 8 \
+ : (P == 3 && WITHIN(B, 20, 21)) ? 10 + (B) - 20 \
+ : -1)
+
+
+
+#define A2_AIN 3
+#define A3_AIN 4
+#define A4_AIN 5
+#define PIN_TO_AIN(P) A##P##_AIN
+#define AIN_TO_RESULT(P) ( (P - HAL_ADC_AIN_START == HAL_ADC_AIN_NUM_SENSORS-1) ? 0 : (P - HAL_ADC_AIN_START + 1) )
diff --git a/Marlin/src/HAL/SAMD21/Servo.cpp b/Marlin/src/HAL/SAMD21/Servo.cpp
new file mode 100644
index 0000000000..38b995fc9a
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/Servo.cpp
@@ -0,0 +1,220 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+
+/**
+ * This comes from Arduino library which at the moment is buggy and uncompilable
+ */
+
+#ifdef __SAMD21__
+
+#include "../../inc/MarlinConfig.h"
+
+#if HAS_SERVOS
+
+#include "../shared/servo.h"
+#include "../shared/servo_private.h"
+#include "SAMD21.h"
+
+#define __TC_GCLK_ID(t) TC##t##_GCLK_ID
+#define _TC_GCLK_ID(t) __TC_GCLK_ID(t)
+#define TC_GCLK_ID _TC_GCLK_ID(SERVO_TC)
+
+#define _TC_PRESCALER(d) TC_CTRLA_PRESCALER_DIV##d##_Val
+#define TC_PRESCALER(d) _TC_PRESCALER(d)
+
+#define __SERVO_IRQn(t) TC##t##_IRQn
+#define _SERVO_IRQn(t) __SERVO_IRQn(t)
+#define SERVO_IRQn _SERVO_IRQn(SERVO_TC)
+
+#define HAL_SERVO_TIMER_ISR() TC_HANDLER(SERVO_TC)
+
+#define TIMER_TCCHANNEL(t) ((t) & 1)
+#define TC_COUNTER_START_VAL 0xFFFF
+
+
+static volatile int8_t currentServoIndex[_Nbr_16timers]; // index for the servo being pulsed for each timer (or -1 if refresh interval)
+
+FORCE_INLINE static uint16_t getTimerCount() {
+ Tcc * const tc = timer_config[SERVO_TC].pTcc;
+
+ tc->CTRLBSET.reg = TCC_CTRLBCLR_CMD_READSYNC;
+ SYNC(tc->STATUS.reg & TC_STATUS_SYNCBUSY);
+
+ return tc->COUNT.bit.COUNT;
+}
+
+// ----------------------------
+// Interrupt handler for the TC
+// ----------------------------
+HAL_SERVO_TIMER_ISR() {
+ Tcc * const tc = timer_config[SERVO_TC].pTcc;
+ const timer16_Sequence_t timer =
+ #ifndef _useTimer1
+ _timer2
+ #elif !defined(_useTimer2)
+ _timer1
+ #else
+ (tc->INTFLAG.reg & tc->INTENSET.reg & TC_INTFLAG_MC0) ? _timer1 : _timer2
+ #endif
+ ;
+ const uint8_t tcChannel = TIMER_TCCHANNEL(timer);
+
+ int8_t cho = currentServoIndex[timer]; // Handle the prior servo first
+ if (cho < 0) { // Servo -1 indicates the refresh interval completed...
+ #if defined(_useTimer1) && defined(_useTimer2)
+ if (currentServoIndex[timer ^ 1] >= 0) {
+ // Wait for both channels
+ // Clear the interrupt
+ tc->INTFLAG.reg = (tcChannel == 0) ? TC_INTFLAG_MC0 : TC_INTFLAG_MC1;
+ return;
+ }
+ #endif
+ tc->COUNT.reg = TC_COUNTER_START_VAL; // ...so reset the timer
+ SYNC(tc->STATUS.reg & TC_STATUS_SYNCBUSY);
+ }
+ else if (SERVO_INDEX(timer, cho) < ServoCount) // prior channel handled?
+ digitalWrite(SERVO(timer, cho).Pin.nbr, LOW); // pulse the prior channel LOW
+
+ currentServoIndex[timer] = ++cho; // go to the next channel (or 0)
+ if (cho < SERVOS_PER_TIMER && SERVO_INDEX(timer, cho) < ServoCount) {
+ if (SERVO(timer, cho).Pin.isActive) // activated?
+ digitalWrite(SERVO(timer, cho).Pin.nbr, HIGH); // yes: pulse HIGH
+
+ tc->CC[tcChannel].reg = getTimerCount() - (uint16_t)SERVO(timer, cho).ticks;
+ }
+ else {
+ // finished all channels so wait for the refresh period to expire before starting over
+ currentServoIndex[timer] = -1; // reset the timer COUNT.reg on the next call
+ const uint16_t cval = getTimerCount() - 256 / (SERVO_TIMER_PRESCALER), // allow 256 cycles to ensure the next CV not missed
+ ival = (TC_COUNTER_START_VAL) - (uint16_t)usToTicks(REFRESH_INTERVAL); // at least REFRESH_INTERVAL has elapsed
+ tc->CC[tcChannel].reg = min(cval, ival);
+ }
+ if (tcChannel == 0) {
+ SYNC(tc->SYNCBUSY.bit.CC0);
+ tc->INTFLAG.reg = TC_INTFLAG_MC0; // Clear the interrupt
+ }
+ else {
+ SYNC(tc->SYNCBUSY.bit.CC1);
+ tc->INTFLAG.reg = TC_INTFLAG_MC1; // Clear the interrupt
+ }
+}
+
+void initISR(const timer16_Sequence_t timer) {
+ Tcc * const tc = timer_config[SERVO_TC].pTcc;
+ const uint8_t tcChannel = TIMER_TCCHANNEL(timer);
+
+ static bool initialized = false; // Servo TC has been initialized
+ if (!initialized) {
+ NVIC_DisableIRQ(SERVO_IRQn);
+
+ // Disable the timer
+ tc->CTRLA.bit.ENABLE = false;
+ SYNC(tc->STATUS.reg & TC_STATUS_SYNCBUSY);
+
+ // Select GCLK0 as timer/counter input clock source
+ GCLK->CLKCTRL.reg =(GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(TCC0_GCLK_ID));
+ SYNC (GCLK->STATUS.bit.SYNCBUSY);
+
+ // Reset the timer
+ tc->CTRLA.bit.SWRST = true;
+ SYNC(tc->CTRLA.bit.SWRST);
+
+ // Set timer counter mode to 16 bits
+ tc->CTRLA.reg = TC_CTRLA_MODE_COUNT16;
+
+ // Set timer counter mode as normal PWM
+ tc->WAVE.bit.WAVEGEN = TCC_WAVE_WAVEGEN_NPWM_Val;
+
+ // Set the prescaler factor
+ tc->CTRLA.bit.PRESCALER = TC_PRESCALER(SERVO_TIMER_PRESCALER);
+
+ // Count down
+ tc->CTRLBSET.reg = TCC_CTRLBCLR_DIR;
+ SYNC(tc->SYNCBUSY.bit.CTRLB);
+
+ // Reset all servo indexes
+ memset((void *)currentServoIndex, 0xFF, sizeof(currentServoIndex));
+
+ // Configure interrupt request
+ NVIC_ClearPendingIRQ(SERVO_IRQn);
+ NVIC_SetPriority(SERVO_IRQn, 5);
+ NVIC_EnableIRQ(SERVO_IRQn);
+
+ initialized = true;
+ }
+
+ if (!tc->CTRLA.bit.ENABLE) {
+ // Reset the timer counter
+ tc->COUNT.reg = TC_COUNTER_START_VAL;
+ SYNC(tc->STATUS.reg & TC_STATUS_SYNCBUSY);
+
+ // Enable the timer and start it
+ tc->CTRLA.bit.ENABLE = true;
+ SYNC(tc->STATUS.reg & TC_STATUS_SYNCBUSY);
+ }
+ // First interrupt request after 1 ms
+ tc->CC[tcChannel].reg = getTimerCount() - (uint16_t)usToTicks(1000UL);
+
+ if (tcChannel == 0 ) {
+ SYNC(tc->SYNCBUSY.bit.CC0);
+
+ // Clear pending match interrupt
+ tc->INTFLAG.reg = TC_INTENSET_MC0;
+ // Enable the match channel interrupt request
+ tc->INTENSET.reg = TC_INTENSET_MC0;
+ }
+ else {
+ SYNC(tc->SYNCBUSY.bit.CC1);
+
+ // Clear pending match interrupt
+ tc->INTFLAG.reg = TC_INTENSET_MC1;
+ // Enable the match channel interrupt request
+ tc->INTENSET.reg = TC_INTENSET_MC1;
+ }
+}
+
+void finISR(const timer16_Sequence_t timer_index) {
+ Tcc * const tc = timer_config[SERVO_TC].pTcc;
+ const uint8_t tcChannel = TIMER_TCCHANNEL(timer_index);
+
+ // Disable the match channel interrupt request
+ tc->INTENCLR.reg = (tcChannel == 0) ? TC_INTENCLR_MC0 : TC_INTENCLR_MC1;
+
+ if (true
+ #if defined(_useTimer1) && defined(_useTimer2)
+ && (tc->INTENCLR.reg & (TC_INTENCLR_MC0|TC_INTENCLR_MC1)) == 0
+ #endif
+ ) {
+ // Disable the timer if not used
+ tc->CTRLA.bit.ENABLE = false;
+ SYNC(tc->STATUS.reg & TC_STATUS_SYNCBUSY);
+ }
+}
+
+#endif // HAS_SERVOS
+
+#endif // __SAMD21__
diff --git a/Marlin/src/HAL/SAMD21/ServoTimers.h b/Marlin/src/HAL/SAMD21/ServoTimers.h
new file mode 100644
index 0000000000..8980547683
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/ServoTimers.h
@@ -0,0 +1,45 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+
+#define _useTimer1
+#define _useTimer2
+
+#define TRIM_DURATION 5 // compensation ticks to trim adjust for digitalWrite delays
+#define SERVO_TIMER_PRESCALER 64 // timer prescaler factor to 64 (avoid overflowing 16-bit clock counter, at 120MHz this is 1831 ticks per millisecond
+
+#define SERVO_TC 3
+
+typedef enum {
+ #ifdef _useTimer1
+ _timer1,
+ #endif
+ #ifdef _useTimer2
+ _timer2,
+ #endif
+ _Nbr_16timers
+} timer16_Sequence_t;
diff --git a/Marlin/src/HAL/SAMD21/eeprom_flash.cpp b/Marlin/src/HAL/SAMD21/eeprom_flash.cpp
new file mode 100644
index 0000000000..4a4e328d1a
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/eeprom_flash.cpp
@@ -0,0 +1,141 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+#ifdef __SAMD21__
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(FLASH_EEPROM_EMULATION)
+
+#define TOTAL_FLASH_SIZE (MARLIN_EEPROM_SIZE+255)/256*256
+
+/* reserve flash memory */
+static const uint8_t flashdata[TOTAL_FLASH_SIZE] __attribute__((__aligned__(256))) { }; \
+
+#include "../shared/eeprom_api.h"
+
+size_t PersistentStore::capacity() {
+ return MARLIN_EEPROM_SIZE;
+ /* const uint8_t psz = NVMCTRL->SEESTAT.bit.PSZ,
+ sblk = NVMCTRL->SEESTAT.bit.SBLK;
+
+ return (!psz && !sblk) ? 0
+ : (psz <= 2) ? (0x200 << psz)
+ : (sblk == 1 || psz == 3) ? 4096
+ : (sblk == 2 || psz == 4) ? 8192
+ : (sblk <= 4 || psz == 5) ? 16384
+ : (sblk >= 9 && psz == 7) ? 65536
+ : 32768;*/
+}
+
+uint32_t PAGE_SIZE;
+uint32_t ROW_SIZE;
+bool hasWritten = false;
+uint8_t * buffer;
+
+void _erase(const volatile void *flash_ptr) {
+ NVMCTRL->ADDR.reg = ((uint32_t)flash_ptr) / 2;
+ NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
+ while (!NVMCTRL->INTFLAG.bit.READY) { }
+
+}
+
+void erase(const volatile void *flash_ptr, uint32_t size) {
+ const uint8_t *ptr = (const uint8_t *)flash_ptr;
+ while (size > ROW_SIZE) {
+ _erase(ptr);
+ ptr += ROW_SIZE;
+ size -= ROW_SIZE;
+ }
+ _erase(ptr);
+}
+
+bool PersistentStore::access_start() {
+ /* clear page buffer*/
+ NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC;
+ while (NVMCTRL->INTFLAG.bit.READY == 0) { }
+
+ PAGE_SIZE = pow(2,3 + NVMCTRL->PARAM.bit.PSZ);
+ ROW_SIZE= PAGE_SIZE * 4;
+ /*NVMCTRL->SEECFG.reg = NVMCTRL_SEECFG_WMODE_BUFFERED; // Buffered mode and segment reallocation active
+ if (NVMCTRL->SEESTAT.bit.RLOCK)
+ NVMCTRL_CMD(NVMCTRL_CTRLB_CMD_USEE); */ // Unlock E2P data write access
+ // erase(&flashdata[0], TOTAL_FLASH_SIZE);
+ return true;
+}
+
+bool PersistentStore::access_finish() {
+ if (hasWritten) {
+ erase(&flashdata[0], TOTAL_FLASH_SIZE);
+
+ NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC;
+ while (NVMCTRL->INTFLAG.bit.READY == 0) { }
+
+ NVMCTRL->CTRLB.bit.MANW = 0;
+
+ volatile uint32_t *dst_addr = (volatile uint32_t *) &flashdata;
+
+ uint32_t *pointer = (uint32_t *) buffer;
+ for (uint32_t i = 0; i < TOTAL_FLASH_SIZE; i+=4) {
+
+ *dst_addr = (uint32_t) *pointer;
+ pointer++;
+ dst_addr ++;
+ }
+
+ // Execute "WP" Write Page
+ NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP;
+ while (NVMCTRL->INTFLAG.bit.READY == 0) { }
+
+ free(buffer);
+ hasWritten = false;
+ }
+ return true;
+}
+
+bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+ if (!hasWritten) {
+ // init temp buffer
+ buffer = (uint8_t *) malloc(MARLIN_EEPROM_SIZE);
+ hasWritten=true;
+ }
+
+ memcpy(buffer+pos,value,size);
+ pos += size;
+ return false;
+}
+
+bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
+ volatile uint8_t *dst_addr = (volatile uint8_t *) &flashdata;
+ dst_addr += pos;
+
+ memcpy(value,(const void *) dst_addr,size);
+ pos += size;
+ return false;
+}
+
+#endif // FLASH_EEPROM_EMULATION
+#endif // __SAMD21__
diff --git a/Marlin/src/HAL/SAMD21/eeprom_qspi.cpp b/Marlin/src/HAL/SAMD21/eeprom_qspi.cpp
new file mode 100644
index 0000000000..587dcb0b14
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/eeprom_qspi.cpp
@@ -0,0 +1,79 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+#ifdef __SAMD21__
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(QSPI_EEPROM)
+
+#error "QSPI_EEPROM emulation Not implemented on SAMD21"
+
+#include "../shared/eeprom_api.h"
+
+#include "QSPIFlash.h"
+
+static bool initialized;
+
+size_t PersistentStore::capacity() { return qspi.size(); }
+
+bool PersistentStore::access_start() {
+ if (!initialized) {
+ qspi.begin();
+ initialized = true;
+ }
+ return true;
+}
+
+bool PersistentStore::access_finish() {
+ qspi.flush();
+ return true;
+}
+
+bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+ while (size--) {
+ const uint8_t v = *value;
+ qspi.writeByte(pos, v);
+ crc16(crc, &v, 1);
+ pos++;
+ value++;
+ }
+ return false;
+}
+
+bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
+ while (size--) {
+ uint8_t c = qspi.readByte(pos);
+ if (writing) *value = c;
+ crc16(crc, &c, 1);
+ pos++;
+ value++;
+ }
+ return false;
+}
+
+#endif // QSPI_EEPROM
+#endif // __SAMD21__
diff --git a/Marlin/src/HAL/SAMD21/eeprom_wired.cpp b/Marlin/src/HAL/SAMD21/eeprom_wired.cpp
new file mode 100644
index 0000000000..ab71e616fc
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/eeprom_wired.cpp
@@ -0,0 +1,82 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+#ifdef __SAMD21__
+
+#include "../../inc/MarlinConfig.h"
+
+#if USE_WIRED_EEPROM
+
+#error "USE_WIRED_EEPROM emulation Not implemented on SAMD21"
+/**
+ * PersistentStore for Arduino-style EEPROM interface
+ * with simple implementations supplied by Marlin.
+ */
+
+#include "../shared/eeprom_if.h"
+#include "../shared/eeprom_api.h"
+
+#ifndef MARLIN_EEPROM_SIZE
+ #error "MARLIN_EEPROM_SIZE is required for I2C / SPI EEPROM."
+#endif
+size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; }
+
+bool PersistentStore::access_start() { eeprom_init(); return true; }
+bool PersistentStore::access_finish() { return true; }
+
+bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+ uint16_t written = 0;
+ while (size--) {
+ const uint8_t v = *value;
+ uint8_t * const p = (uint8_t * const)pos;
+ if (v != eeprom_read_byte(p)) { // EEPROM has only ~100,000 write cycles, so only write bytes that have changed!
+ eeprom_write_byte(p, v);
+ if (++written & 0x7F) delay(2); else safe_delay(2); // Avoid triggering watchdog during long EEPROM writes
+ if (eeprom_read_byte(p) != v) {
+ SERIAL_ECHO_MSG(STR_ERR_EEPROM_WRITE);
+ return true;
+ }
+ }
+ crc16(crc, &v, 1);
+ pos++;
+ value++;
+ }
+ return false;
+}
+
+bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
+ while (size--) {
+ uint8_t c = eeprom_read_byte((uint8_t*)pos);
+ if (writing) *value = c;
+ crc16(crc, &c, 1);
+ pos++;
+ value++;
+ }
+ return false;
+}
+
+#endif // USE_WIRED_EEPROM
+#endif // __SAMD21__
diff --git a/Marlin/src/HAL/SAMD21/endstop_interrupts.h b/Marlin/src/HAL/SAMD21/endstop_interrupts.h
new file mode 100644
index 0000000000..d8711aa018
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/endstop_interrupts.h
@@ -0,0 +1,253 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+
+/**
+ * Endstop interrupts for ATMEL SAMD21 based targets.
+ *
+ * On SAMD21, all pins support external interrupt capability.
+ * Any pin can be used for external interrupts, but there are some restrictions.
+ * At most 16 different external interrupts can be used at one time.
+ * Further, you can’t just pick any 16 pins to use. This is because every pin on the SAMD21
+ * connects to what is called an EXTINT line, and only one pin per EXTINT line can be used for external
+ * interrupts at a time
+ */
+
+/**
+ * Endstop Interrupts
+ *
+ * Without endstop interrupts the endstop pins must be polled continually in
+ * the temperature-ISR via endstops.update(), most of the time finding no change.
+ * With this feature endstops.update() is called only when we know that at
+ * least one endstop has changed state, saving valuable CPU cycles.
+ *
+ * This feature only works when all used endstop pins can generate an 'external interrupt'.
+ *
+ * Test whether pins issue interrupts on your board by flashing 'pin_interrupt_test.ino'.
+ * (Located in Marlin/buildroot/share/pin_interrupt_test/pin_interrupt_test.ino)
+ */
+
+#include "../../module/endstops.h"
+
+#define MATCH_EILINE(P1,P2) (P1 != P2 && PIN_TO_EILINE(P1) == PIN_TO_EILINE(P2))
+#define MATCH_X_MAX_EILINE(P) TERN0(HAS_X_MAX, DEFER4(MATCH_EILINE)(P, X_MAX_PIN))
+#define MATCH_X_MIN_EILINE(P) TERN0(HAS_X_MIN, DEFER4(MATCH_EILINE)(P, X_MIN_PIN))
+#define MATCH_Y_MAX_EILINE(P) TERN0(HAS_Y_MAX, DEFER4(MATCH_EILINE)(P, Y_MAX_PIN))
+#define MATCH_Y_MIN_EILINE(P) TERN0(HAS_Y_MIN, DEFER4(MATCH_EILINE)(P, Y_MIN_PIN))
+#define MATCH_Z_MAX_EILINE(P) TERN0(HAS_Z_MAX, DEFER4(MATCH_EILINE)(P, Z_MAX_PIN))
+#define MATCH_Z_MIN_EILINE(P) TERN0(HAS_Z_MIN, DEFER4(MATCH_EILINE)(P, Z_MIN_PIN))
+#define MATCH_I_MAX_EILINE(P) TERN0(HAS_I_MAX, DEFER4(MATCH_EILINE)(P, I_MAX_PIN))
+#define MATCH_I_MIN_EILINE(P) TERN0(HAS_I_MIN, DEFER4(MATCH_EILINE)(P, I_MIN_PIN))
+#define MATCH_J_MAX_EILINE(P) TERN0(HAS_J_MAX, DEFER4(MATCH_EILINE)(P, J_MAX_PIN))
+#define MATCH_J_MIN_EILINE(P) TERN0(HAS_J_MIN, DEFER4(MATCH_EILINE)(P, J_MIN_PIN))
+#define MATCH_K_MAX_EILINE(P) TERN0(HAS_K_MAX, DEFER4(MATCH_EILINE)(P, K_MAX_PIN))
+#define MATCH_K_MIN_EILINE(P) TERN0(HAS_K_MIN, DEFER4(MATCH_EILINE)(P, K_MIN_PIN))
+#define MATCH_U_MAX_EILINE(P) TERN0(HAS_U_MAX, DEFER4(MATCH_EILINE)(P, U_MAX_PIN))
+#define MATCH_U_MIN_EILINE(P) TERN0(HAS_U_MIN, DEFER4(MATCH_EILINE)(P, U_MIN_PIN))
+#define MATCH_V_MAX_EILINE(P) TERN0(HAS_V_MAX, DEFER4(MATCH_EILINE)(P, V_MAX_PIN))
+#define MATCH_V_MIN_EILINE(P) TERN0(HAS_V_MIN, DEFER4(MATCH_EILINE)(P, V_MIN_PIN))
+#define MATCH_W_MAX_EILINE(P) TERN0(HAS_W_MAX, DEFER4(MATCH_EILINE)(P, W_MAX_PIN))
+#define MATCH_W_MIN_EILINE(P) TERN0(HAS_W_MIN, DEFER4(MATCH_EILINE)(P, W_MIN_PIN))
+#define MATCH_Z2_MAX_EILINE(P) TERN0(HAS_Z2_MAX, DEFER4(MATCH_EILINE)(P, Z2_MAX_PIN))
+#define MATCH_Z2_MIN_EILINE(P) TERN0(HAS_Z2_MIN, DEFER4(MATCH_EILINE)(P, Z2_MIN_PIN))
+#define MATCH_Z3_MAX_EILINE(P) TERN0(HAS_Z3_MAX, DEFER4(MATCH_EILINE)(P, Z3_MAX_PIN))
+#define MATCH_Z3_MIN_EILINE(P) TERN0(HAS_Z3_MIN, DEFER4(MATCH_EILINE)(P, Z3_MIN_PIN))
+#define MATCH_Z4_MAX_EILINE(P) TERN0(HAS_Z4_MAX, DEFER4(MATCH_EILINE)(P, Z4_MAX_PIN))
+#define MATCH_Z4_MIN_EILINE(P) TERN0(HAS_Z4_MIN, DEFER4(MATCH_EILINE)(P, Z4_MIN_PIN))
+#define MATCH_Z_MIN_PROBE_EILINE(P) TERN0(HAS_Z_MIN_PROBE_PIN, DEFER4(MATCH_EILINE)(P, Z_MIN_PROBE_PIN))
+
+#define AVAILABLE_EILINE(P) ( PIN_TO_EILINE(P) != -1 \
+ && !MATCH_X_MAX_EILINE(P) && !MATCH_X_MIN_EILINE(P) \
+ && !MATCH_Y_MAX_EILINE(P) && !MATCH_Y_MIN_EILINE(P) \
+ && !MATCH_Z_MAX_EILINE(P) && !MATCH_Z_MIN_EILINE(P) \
+ && !MATCH_I_MAX_EILINE(P) && !MATCH_I_MIN_EILINE(P) \
+ && !MATCH_J_MAX_EILINE(P) && !MATCH_J_MIN_EILINE(P) \
+ && !MATCH_K_MAX_EILINE(P) && !MATCH_K_MIN_EILINE(P) \
+ && !MATCH_U_MAX_EILINE(P) && !MATCH_U_MIN_EILINE(P) \
+ && !MATCH_V_MAX_EILINE(P) && !MATCH_V_MIN_EILINE(P) \
+ && !MATCH_W_MAX_EILINE(P) && !MATCH_W_MIN_EILINE(P) \
+ && !MATCH_Z2_MAX_EILINE(P) && !MATCH_Z2_MIN_EILINE(P) \
+ && !MATCH_Z3_MAX_EILINE(P) && !MATCH_Z3_MIN_EILINE(P) \
+ && !MATCH_Z4_MAX_EILINE(P) && !MATCH_Z4_MIN_EILINE(P) \
+ && !MATCH_Z_MIN_PROBE_EILINE(P) )
+
+// One ISR for all EXT-Interrupts
+void endstop_ISR() { endstops.update(); }
+
+void setup_endstop_interrupts() {
+ #define _ATTACH(P) attachInterrupt(P, endstop_ISR, CHANGE)
+ #if HAS_X_MAX
+ #if !AVAILABLE_EILINE(X_MAX_PIN)
+ #error "X_MAX_PIN has no EXTINT line available."
+ #endif
+ _ATTACH(X_MAX_PIN);
+ #endif
+ #if HAS_X_MIN
+ #if !AVAILABLE_EILINE(X_MIN_PIN)
+ #error "X_MIN_PIN has no EXTINT line available."
+ #endif
+ _ATTACH(X_MIN_PIN);
+ #endif
+ #if HAS_Y_MAX
+ #if !AVAILABLE_EILINE(Y_MAX_PIN)
+ #error "Y_MAX_PIN has no EXTINT line available."
+ #endif
+ _ATTACH(Y_MAX_PIN);
+ #endif
+ #if HAS_Y_MIN
+ #if !AVAILABLE_EILINE(Y_MIN_PIN)
+ #error "Y_MIN_PIN has no EXTINT line available."
+ #endif
+ _ATTACH(Y_MIN_PIN);
+ #endif
+ #if HAS_Z_MAX
+ #if !AVAILABLE_EILINE(Z_MAX_PIN)
+ #error "Z_MAX_PIN has no EXTINT line available."
+ #endif
+ _ATTACH(Z_MAX_PIN);
+ #endif
+ #if HAS_Z_MIN
+ #if !AVAILABLE_EILINE(Z_MIN_PIN)
+ #error "Z_MIN_PIN has no EXTINT line available."
+ #endif
+ _ATTACH(Z_MIN_PIN);
+ #endif
+ #if HAS_Z2_MAX
+ #if !AVAILABLE_EILINE(Z2_MAX_PIN)
+ #error "Z2_MAX_PIN has no EXTINT line available."
+ #endif
+ _ATTACH(Z2_MAX_PIN);
+ #endif
+ #if HAS_Z2_MIN
+ #if !AVAILABLE_EILINE(Z2_MIN_PIN)
+ #error "Z2_MIN_PIN has no EXTINT line available."
+ #endif
+ _ATTACH(Z2_MIN_PIN);
+ #endif
+ #if HAS_Z3_MAX
+ #if !AVAILABLE_EILINE(Z3_MAX_PIN)
+ #error "Z3_MAX_PIN has no EXTINT line available."
+ #endif
+ _ATTACH(Z3_MAX_PIN);
+ #endif
+ #if HAS_Z3_MIN
+ #if !AVAILABLE_EILINE(Z3_MIN_PIN)
+ #error "Z3_MIN_PIN has no EXTINT line available."
+ #endif
+ _ATTACH(Z3_MIN_PIN);
+ #endif
+ #if HAS_Z4_MAX
+ #if !AVAILABLE_EILINE(Z4_MAX_PIN)
+ #error "Z4_MAX_PIN has no EXTINT line available."
+ #endif
+ _ATTACH(Z4_MAX_PIN);
+ #endif
+ #if HAS_Z4_MIN
+ #if !AVAILABLE_EILINE(Z4_MIN_PIN)
+ #error "Z4_MIN_PIN has no EXTINT line available."
+ #endif
+ _ATTACH(Z4_MIN_PIN);
+ #endif
+ #if HAS_Z_MIN_PROBE_PIN
+ #if !AVAILABLE_EILINE(Z_MIN_PROBE_PIN)
+ #error "Z_MIN_PROBE_PIN has no EXTINT line available."
+ #endif
+ _ATTACH(Z_MIN_PROBE_PIN);
+ #endif
+ #if HAS_I_MAX
+ #if !AVAILABLE_EILINE(I_MAX_PIN)
+ #error "I_MAX_PIN has no EXTINT line available."
+ #endif
+ attachInterrupt(I_MAX_PIN, endstop_ISR, CHANGE);
+ #endif
+ #if HAS_I_MIN
+ #if !AVAILABLE_EILINE(I_MIN_PIN)
+ #error "I_MIN_PIN has no EXTINT line available."
+ #endif
+ attachInterrupt(I_MIN_PIN, endstop_ISR, CHANGE);
+ #endif
+ #if HAS_J_MAX
+ #if !AVAILABLE_EILINE(J_MAX_PIN)
+ #error "J_MAX_PIN has no EXTINT line available."
+ #endif
+ attachInterrupt(J_MAX_PIN, endstop_ISR, CHANGE);
+ #endif
+ #if HAS_J_MIN
+ #if !AVAILABLE_EILINE(J_MIN_PIN)
+ #error "J_MIN_PIN has no EXTINT line available."
+ #endif
+ attachInterrupt(J_MIN_PIN, endstop_ISR, CHANGE);
+ #endif
+ #if HAS_K_MAX
+ #if !AVAILABLE_EILINE(K_MAX_PIN)
+ #error "K_MAX_PIN has no EXTINT line available."
+ #endif
+ attachInterrupt(K_MAX_PIN, endstop_ISR, CHANGE);
+ #endif
+ #if HAS_K_MIN
+ #if !AVAILABLE_EILINE(K_MIN_PIN)
+ #error "K_MIN_PIN has no EXTINT line available."
+ #endif
+ attachInterrupt(K_MIN_PIN, endstop_ISR, CHANGE);
+ #endif
+ #if HAS_U_MAX
+ #if !AVAILABLE_EILINE(U_MAX_PIN)
+ #error "U_MAX_PIN has no EXTINT line available."
+ #endif
+ attachInterrupt(U_MAX_PIN, endstop_ISR, CHANGE);
+ #endif
+ #if HAS_U_MIN
+ #if !AVAILABLE_EILINE(U_MIN_PIN)
+ #error "U_MIN_PIN has no EXTINT line available."
+ #endif
+ attachInterrupt(U_MIN_PIN, endstop_ISR, CHANGE);
+ #endif
+ #if HAS_V_MAX
+ #if !AVAILABLE_EILINE(V_MAX_PIN)
+ #error "V_MAX_PIN has no EXTINT line available."
+ #endif
+ attachInterrupt(V_MAX_PIN, endstop_ISR, CHANGE);
+ #endif
+ #if HAS_V_MIN
+ #if !AVAILABLE_EILINE(V_MIN_PIN)
+ #error "V_MIN_PIN has no EXTINT line available."
+ #endif
+ attachInterrupt(V_MIN_PIN, endstop_ISR, CHANGE);
+ #endif
+ #if HAS_W_MAX
+ #if !AVAILABLE_EILINE(W_MAX_PIN)
+ #error "W_MAX_PIN has no EXTINT line available."
+ #endif
+ attachInterrupt(W_MAX_PIN, endstop_ISR, CHANGE);
+ #endif
+ #if HAS_W_MIN
+ #if !AVAILABLE_EILINE(W_MIN_PIN)
+ #error "W_MIN_PIN has no EXTINT line available."
+ #endif
+ attachInterrupt(W_MIN_PIN, endstop_ISR, CHANGE);
+ #endif
+}
diff --git a/Marlin/src/HAL/SAMD21/fastio.h b/Marlin/src/HAL/SAMD21/fastio.h
new file mode 100644
index 0000000000..db64f2166f
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/fastio.h
@@ -0,0 +1,216 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+
+/**
+ * Fast IO functions for SAMD21
+ */
+
+#include "SAMD21.h"
+
+/**
+ * Utility functions
+ */
+
+#ifndef MASK
+ #define MASK(PIN) _BV(PIN)
+#endif
+
+/**
+ * Magic I/O routines
+ *
+ * Now you can simply SET_OUTPUT(IO); WRITE(IO, HIGH); WRITE(IO, LOW);
+ */
+
+// Read a pin
+#define READ(IO) ((PORT->Group[(EPortType)GET_SAMD_PORT(IO)].IN.reg & MASK(GET_SAMD_PIN(IO))) != 0)
+
+// Write to a pin
+#define WRITE(IO,V) do{ \
+ const EPortType port = (EPortType)GET_SAMD_PORT(IO); \
+ const uint32_t mask = MASK(GET_SAMD_PIN(IO)); \
+ \
+ if (V) PORT->Group[port].OUTSET.reg = mask; \
+ else PORT->Group[port].OUTCLR.reg = mask; \
+ }while(0)
+
+// Toggle a pin
+#define TOGGLE(IO) PORT->Group[(EPortType)GET_SAMD_PORT(IO)].OUTTGL.reg = MASK(GET_SAMD_PIN(IO));
+
+// Set pin as input
+#define SET_INPUT(IO) do{ \
+ const EPortType port = (EPortType)GET_SAMD_PORT(IO); \
+ const uint32_t pin = GET_SAMD_PIN(IO); \
+ \
+ PORT->Group[port].PINCFG[pin].reg = (uint8_t)(PORT_PINCFG_INEN); \
+ PORT->Group[port].DIRCLR.reg = MASK(pin); \
+ }while(0)
+// Set pin as input with pullup
+#define SET_INPUT_PULLUP(IO) do{ \
+ const EPortType port = (EPortType)GET_SAMD_PORT(IO); \
+ const uint32_t pin = GET_SAMD_PIN(IO); \
+ const uint32_t mask = MASK(pin); \
+ \
+ PORT->Group[port].PINCFG[pin].reg = (uint8_t)(PORT_PINCFG_INEN | PORT_PINCFG_PULLEN); \
+ PORT->Group[port].DIRCLR.reg = mask; \
+ PORT->Group[port].OUTSET.reg = mask; \
+ }while(0)
+// Set pin as input with pulldown
+#define SET_INPUT_PULLDOWN(IO) do{ \
+ const EPortType port = (EPortType)GET_SAMD_PORT(IO); \
+ const uint32_t pin = GET_SAMD_PIN(IO); \
+ const uint32_t mask = MASK(pin); \
+ \
+ PORT->Group[port].PINCFG[pin].reg = (uint8_t)(PORT_PINCFG_INEN | PORT_PINCFG_PULLEN); \
+ PORT->Group[port].DIRCLR.reg = mask; \
+ PORT->Group[port].OUTCLR.reg = mask; \
+ }while(0)
+// Set pin as output (push pull)
+#define SET_OUTPUT(IO) do{ \
+ const EPortType port = (EPortType)GET_SAMD_PORT(IO); \
+ const uint32_t pin = GET_SAMD_PIN(IO); \
+ \
+ PORT->Group[port].DIRSET.reg = MASK(pin); \
+ PORT->Group[port].PINCFG[pin].reg = 0; \
+ }while(0)
+// Set pin as output (open drain)
+#define SET_OUTPUT_OD(IO) do{ \
+ const EPortType port = (EPortType)GET_SAMD_PORT(IO); \
+ const uint32_t pin = GET_SAMD_PIN(IO); \
+ \
+ PORT->Group[port].PINCFG[pin].reg = (uint8_t)(PORT_PINCFG_PULLEN); \
+ PORT->Group[port].DIRCLR.reg = MASK(pin); \
+ }while(0)
+// Set pin as PWM (push pull)
+#define SET_PWM SET_OUTPUT
+// Set pin as PWM (open drain)
+#define SET_PWM_OD SET_OUTPUT_OD
+
+// check if pin is an output
+#define IS_OUTPUT(IO) ((PORT->Group[(EPortType)GET_SAMD_PORT(IO)].DIR.reg & MASK(GET_SAMD_PIN(IO))) \
+ || (PORT->Group[(EPortType)GET_SAMD_PORT(IO)].PINCFG[GET_SAMD_PIN(IO)].reg & (PORT_PINCFG_INEN | PORT_PINCFG_PULLEN)) == PORT_PINCFG_PULLEN)
+// check if pin is an input
+#define IS_INPUT(IO) !IS_OUTPUT(IO)
+
+// Shorthand
+#define OUT_WRITE(IO,V) do{ SET_OUTPUT(IO); WRITE(IO,V); }while(0)
+#define OUT_WRITE_OD(IO,V) do{ SET_OUTPUT_OD(IO); WRITE(IO,V); }while(0)
+
+// digitalRead/Write wrappers
+#define extDigitalRead(IO) digitalRead(IO)
+#define extDigitalWrite(IO,V) digitalWrite(IO,V)
+
+/**
+ * Ports and functions
+ * Added as necessary or if I feel like it- not a comprehensive list!
+ */
+
+/*
+ * Some of these share the same source and so can't be used in the same time
+ */
+#define PWM_PIN(P) (WITHIN(P, 2, 13) || WITHIN(P, 22, 23) || WITHIN(P, 44, 45) || P == 48)
+
+// Return fulfilled ADCx->INPUTCTRL.reg
+#define PIN_TO_INPUTCTRL(P) ( (P == 0) ? ADC_INPUTCTRL_MUXPOS_PIN0 \
+ : ((P) == 1) ? ADC_INPUTCTRL_MUXPOS_PIN1 \
+ : ((P) == 2) ? ADC_INPUTCTRL_MUXPOS_PIN3 \
+ : ((P) == 3) ? ADC_INPUTCTRL_MUXPOS_PIN4 \
+ : ((P) == 4) ? ADC_INPUTCTRL_MUXPOS_PIN5 \
+ : ((P) == 5) ? ADC_INPUTCTRL_MUXPOS_PIN5 \
+ : ((P) == 6) ? ADC_INPUTCTRL_MUXPOS_PIN6 \
+ : ((P) == 7) ? ADC_INPUTCTRL_MUXPOS_PIN7 \
+ : ((P) == 8) ? ADC_INPUTCTRL_MUXPOS_PIN8 \
+ : ((P) == 9) ? ADC_INPUTCTRL_MUXPOS_PIN9 \
+ : ((P) == 10) ? ADC_INPUTCTRL_MUXPOS_PIN10 \
+ : ((P) == 11) ? ADC_INPUTCTRL_MUXPOS_PIN11 \
+ : ((P) == 12) ? ADC_INPUTCTRL_MUXPOS_PIN12 \
+ : ((P) == 13) ? ADC_INPUTCTRL_MUXPOS_PIN13 \
+ : ((P) == 14) ? ADC_INPUTCTRL_MUXPOS_PIN14 \
+ : ADC_INPUTCTRL_MUXPOS_PIN15)
+
+#define digitalPinToAnalogInput(P) (WITHIN(P, 67, 74) ? (P) - 67 : WITHIN(P, 54, 61) ? 8 + (P) - 54 : WITHIN(P, 12, 13) ? 16 + (P) - 12 : P == 9 ? 18 : -1)
+
+/**
+ * pins
+ */
+
+// PORTA
+#define DIO28_PIN PIN_PA02 // A0
+#define DIO56_PIN PIN_PA03 // A13
+#define DIO31_PIN PIN_PA04 // A13
+#define DIO32_PIN PIN_PA05 // A1
+#define DIO8_PIN PIN_PA06 // A14
+#define DIO9_PIN PIN_PA07 // A15
+#define DIO4_PIN PIN_PA08 // A15
+#define DIO3_PIN PIN_PA09 // A15
+#define DIO1_PIN PIN_PA10
+#define DIO0_PIN PIN_PA11
+#define DIO18_PIN PIN_PA12
+#define DIO52_PIN PIN_PA13
+#define DIO2_PIN PIN_PA14
+#define DIO5_PIN PIN_PA15
+#define DIO11_PIN PIN_PA16
+#define DIO13_PIN PIN_PA17
+#define DIO10_PIN PIN_PA18
+#define DIO12_PIN PIN_PA19
+#define DIO6_PIN PIN_PA20
+#define DIO07_PIN PIN_PA21
+#define DIO34_PIN PIN_PA22
+#define DIO35_PIN PIN_PA23
+#define DIO42_PIN PIN_PA24
+#define DIO43_PIN PIN_PA25
+
+#define DIO40_PIN PIN_PA27
+
+#define DIO26_PIN PIN_PB00
+#define DIO27_PIN PIN_PB01 // A0
+#define DIO33_PIN PIN_PB02
+#define DIO39_PIN PIN_PB03
+#define DIO14_PIN PIN_PB04
+#define DIO15_PIN PIN_PB05
+#define DIO16_PIN PIN_PB06
+#define DIO17_PIN PIN_PB07
+#define DIO29_PIN PIN_PB08
+#define DIO30_PIN PIN_PB09
+#define DIO37_PIN PIN_PB10
+#define DIO38_PIN PIN_PB11
+#define DIO36_PIN PIN_PB12
+#define DIO19_PIN PIN_PB13
+#define DIO20_PIN PIN_PB14
+#define DIO21_PIN PIN_PB15
+#define DIO22_PIN PIN_PB16
+#define DIO23_PIN PIN_PB17
+
+#define DIO44_PIN PIN_PB22
+#define DIO45_PIN PIN_PB23
+#define DIO24_PIN PIN_PB30
+#define DIO25_PIN PIN_PB31
+
+#define DIO53_PIN PIN_PA21
+#define DIO54_PIN PIN_PA06
+#define DIO55_PIN PIN_PA07
+
diff --git a/Marlin/src/HAL/SAMD21/inc/Conditionals_LCD.h b/Marlin/src/HAL/SAMD21/inc/Conditionals_LCD.h
new file mode 100644
index 0000000000..ca467937c3
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/inc/Conditionals_LCD.h
@@ -0,0 +1,31 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+#pragma once
+
+#if HAS_SPI_TFT || HAS_FSMC_TFT
+ #error "Sorry! TFT displays are not available for HAL/SAMD21."
+#endif
diff --git a/Marlin/src/HAL/SAMD21/inc/Conditionals_adv.h b/Marlin/src/HAL/SAMD21/inc/Conditionals_adv.h
new file mode 100644
index 0000000000..d6a3c4fe0b
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/inc/Conditionals_adv.h
@@ -0,0 +1,27 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+#pragma once
diff --git a/Marlin/src/HAL/SAMD21/inc/Conditionals_post.h b/Marlin/src/HAL/SAMD21/inc/Conditionals_post.h
new file mode 100644
index 0000000000..7315dc12a7
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/inc/Conditionals_post.h
@@ -0,0 +1,33 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+#pragma once
+
+#if USE_FALLBACK_EEPROM
+ #define FLASH_EEPROM_EMULATION
+#elif EITHER(I2C_EEPROM, SPI_EEPROM)
+ #define USE_SHARED_EEPROM 1
+#endif
diff --git a/Marlin/src/HAL/SAMD21/inc/SanityCheck.h b/Marlin/src/HAL/SAMD21/inc/SanityCheck.h
new file mode 100644
index 0000000000..95fa5e5940
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/inc/SanityCheck.h
@@ -0,0 +1,50 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+
+/**
+ * Test SAMD21 specific configuration values for errors at compile-time.
+ */
+
+#if SERVO_TC == MF_TIMER_RTC
+ #error "Servos can't use RTC timer"
+#endif
+
+#if ENABLED(EMERGENCY_PARSER)
+ #error "EMERGENCY_PARSER is not yet implemented for SAMD21. Disable EMERGENCY_PARSER to continue."
+#endif
+
+#if ENABLED(SDIO_SUPPORT)
+ #error "SDIO_SUPPORT is not supported on SAMD21."
+#endif
+
+#if ENABLED(FAST_PWM_FAN)
+ #error "Features requiring Hardware PWM (FAST_PWM_FAN) are not yet supported on SAMD21."
+#endif
+
+#if ENABLED(POSTMORTEM_DEBUGGING)
+ #error "POSTMORTEM_DEBUGGING is not yet supported on SAMD21."
+#endif
diff --git a/Marlin/src/HAL/SAMD21/pinsDebug.h b/Marlin/src/HAL/SAMD21/pinsDebug.h
new file mode 100644
index 0000000000..f94315cdf5
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/pinsDebug.h
@@ -0,0 +1,160 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+
+#define NUMBER_PINS_TOTAL PINS_COUNT
+
+#define digitalRead_mod(p) extDigitalRead(p)
+#define PRINT_PORT(p) do{ SERIAL_ECHOPGM(" Port: "); sprintf_P(buffer, PSTR("%c%02ld"), 'A' + g_APinDescription[p].ulPort, g_APinDescription[p].ulPin); SERIAL_ECHO(buffer); }while (0)
+#define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0)
+#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%3d "), p); SERIAL_ECHO(buffer); }while(0)
+#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0)
+#define GET_ARRAY_PIN(p) pin_array[p].pin
+#define GET_ARRAY_IS_DIGITAL(p) pin_array[p].is_digital
+#define VALID_PIN(pin) (pin >= 0 && pin < (int8_t)NUMBER_PINS_TOTAL)
+#define DIGITAL_PIN_TO_ANALOG_PIN(p) digitalPinToAnalogInput(p)
+#define IS_ANALOG(P) (DIGITAL_PIN_TO_ANALOG_PIN(P)!=-1)
+#define pwm_status(pin) digitalPinHasPWM(pin)
+#define MULTI_NAME_PAD 27 // space needed to be pretty if not first name assigned to a pin
+
+// pins that will cause hang/reset/disconnect in M43 Toggle and Watch utilities
+// uses pin index
+#define M43_NEVER_TOUCH(Q) ((Q) >= 75)
+
+bool GET_PINMODE(int8_t pin) { // 1: output, 0: input
+ const EPortType samdport = g_APinDescription[pin].ulPort;
+ const uint32_t samdpin = g_APinDescription[pin].ulPin;
+ return PORT->Group[samdport].DIR.reg & MASK(samdpin) || (PORT->Group[samdport].PINCFG[samdpin].reg & (PORT_PINCFG_INEN | PORT_PINCFG_PULLEN)) == PORT_PINCFG_PULLEN;
+}
+
+void pwm_details(int32_t pin) {
+ if (pwm_status(pin)) {
+ //uint32_t chan = g_APinDescription[pin].ulPWMChannel TODO when fast pwm is operative;
+ //SERIAL_ECHOPGM("PWM = ", duty);
+ }
+}
+
+/**
+ * SAMD21 Board pin| PORT | Label
+ * ----------------+--------+-------
+ * 0 | PB25 | "RX0"
+ * 1 | PB24 | "TX0"
+ * 2 | PC18 |
+ * 3 | PC19 |
+ * 4 | PC20 |
+ * 5 | PC21 |
+ * 6 | PD20 |
+ * 7 | PD21 |
+ * 8 | PB18 |
+ * 9 | PB2 |
+ * 10 | PB22 |
+ * 11 | PB23 |
+ * 12 | PB0 | "A16"
+ * 13 | PB1 | LED AMBER "L" / "A17"
+ * 14 | PB16 | "TX3"
+ * 15 | PB17 | "RX3"
+ * 16 | PC22 | "TX2"
+ * 17 | PC23 | "RX2"
+ * 18 | PB12 | "TX1" / "A18"
+ * 19 | PB13 | "RX1"
+ * 20 | PB20 | "SDA"
+ * 21 | PB21 | "SCL"
+ * 22 | PD12 |
+ * 23 | PA15 |
+ * 24 | PC17 |
+ * 25 | PC16 |
+ * 26 | PA12 |
+ * 27 | PA13 |
+ * 28 | PA14 |
+ * 29 | PB19 |
+ * 30 | PA23 |
+ * 31 | PA22 |
+ * 32 | PA21 |
+ * 33 | PA20 |
+ * 34 | PA19 |
+ * 35 | PA18 |
+ * 36 | PA17 |
+ * 37 | PA16 |
+ * 38 | PB15 |
+ * 39 | PB14 |
+ * 40 | PC13 |
+ * 41 | PC12 |
+ * 42 | PC15 |
+ * 43 | PC14 |
+ * 44 | PC11 |
+ * 45 | PC10 |
+ * 46 | PC6 |
+ * 47 | PC7 |
+ * 48 | PC4 |
+ * 49 | PC5 |
+ * 50 | PD11 |
+ * 51 | PD8 |
+ * 52 | PD9 |
+ * 53 | PD10 |
+ * 54 | PB5 | "A8"
+ * 55 | PB6 | "A9"
+ * 56 | PB7 | "A10"
+ * 57 | PB8 | "A11"
+ * 58 | PB9 | "A12"
+ * 69 | PA4 | "A13"
+ * 60 | PA6 | "A14"
+ * 61 | PA7 | "A15"
+ * 62 | PB17 |
+ * 63 | PB20 |
+ * 64 | PD11 |
+ * 65 | PD8 |
+ * 66 | PD9 |
+ * 67 | PA2 | "A0" / "DAC0"
+ * 68 | PA5 | "A1" / "DAC1"
+ * 69 | PB3 | "A2"
+ * 70 | PC0 | "A3"
+ * 71 | PC1 | "A4"
+ * 72 | PC2 | "A5"
+ * 73 | PC3 | "A6"
+ * 74 | PB4 | "A7"
+ * 75 | PC31 | LED GREEN "RX"
+ * 76 | PC30 | LED GREEN "TX"
+ * 77 | PA27 | USB: Host enable
+ * 78 | PA24 | USB: D-
+ * 79 | PA25 | USB: D+
+ * 80 | PB29 | SD: MISO
+ * 81 | PB27 | SD: SCK
+ * 82 | PB26 | SD: MOSI
+ * 83 | PB28 | SD: CS
+ * 84 | PA3 | AREF
+ * 85 | PA2 | DAC0 (Duplicate)
+ * 86 | PA5 | DAC1 (Duplicate)
+ * 87 | PB1 | LED AMBER "L" (Duplicate)
+ * 88 | PC24 | NeoPixel
+ * 89 | PB10 | QSPI: SCK
+ * 90 | PB11 | QSPI: CS
+ * 91 | PA8 | QSPI: IO0
+ * 92 | PA9 | QSPI: IO1
+ * 93 | PA10 | QSPI: IO2
+ * 94 | PA11 | QSPI: IO3
+ * 95 | PB31 | SD: DETECT
+ */
diff --git a/Marlin/src/HAL/SAMD21/spi_pins.h b/Marlin/src/HAL/SAMD21/spi_pins.h
new file mode 100644
index 0000000000..8c25b84dc1
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/spi_pins.h
@@ -0,0 +1,54 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+
+/**
+ * SAMD21 Default SPI Pins
+ *
+ * SS SCK MISO MOSI
+ * +-------------------------+
+ * SPI | 53 52 50 51 |
+ * SPI1 | 83 81 80 82 |
+ * +-------------------------+
+ * Any pin can be used for Chip Select (SD_SS_PIN)
+ */
+#ifndef SD_SCK_PIN
+ #define SD_SCK_PIN 38
+#endif
+#ifndef SD_MISO_PIN
+ #define SD_MISO_PIN 36
+#endif
+#ifndef SD_MOSI_PIN
+ #define SD_MOSI_PIN 37
+#endif
+#ifndef SDSS
+ #define SDSS 18
+#endif
+
+#ifndef SD_SS_PIN
+ #define SD_SS_PIN SDSS
+#endif
diff --git a/Marlin/src/HAL/SAMD21/timers.cpp b/Marlin/src/HAL/SAMD21/timers.cpp
new file mode 100644
index 0000000000..82fa162f17
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/timers.cpp
@@ -0,0 +1,217 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+#ifdef __SAMD21__
+
+// --------------------------------------------------------------------------
+// Includes
+// --------------------------------------------------------------------------
+
+#include "../../inc/MarlinConfig.h"
+#include "ServoTimers.h" // for SERVO_TC
+
+// --------------------------------------------------------------------------
+// Local defines
+// --------------------------------------------------------------------------
+
+#define NUM_HARDWARE_TIMERS 9
+
+// --------------------------------------------------------------------------
+// Private Variables
+// --------------------------------------------------------------------------
+
+const tTimerConfig timer_config[NUM_HARDWARE_TIMERS] = {
+ { {.pTcc=TCC0}, TimerType::tcc, TCC0_IRQn, TC_PRIORITY(0) }, // 0 - stepper (assigned priority 2)
+ { {.pTcc=TCC1}, TimerType::tcc, TCC1_IRQn, TC_PRIORITY(1) }, // 1 - stepper (needed by 32 bit timers)
+ { {.pTcc=TCC2}, TimerType::tcc, TCC2_IRQn, 5 }, // 2 - tone (reserved by framework and fixed assigned priority 5)
+ { {.pTc=TC3}, TimerType::tc, TC3_IRQn, TC_PRIORITY(3) }, // 3 - servo (assigned priority 1)
+ { {.pTc=TC4}, TimerType::tc, TC4_IRQn, TC_PRIORITY(4) }, // 4 - software serial (no interrupts used)
+ { {.pTc=TC5}, TimerType::tc, TC5_IRQn, TC_PRIORITY(5) },
+ { {.pTc=TC6}, TimerType::tc, TC6_IRQn, TC_PRIORITY(6) },
+ { {.pTc=TC7}, TimerType::tc, TC7_IRQn, TC_PRIORITY(7) },
+ { {.pRtc=RTC}, TimerType::rtc, RTC_IRQn, TC_PRIORITY(8) } // 8 - temperature (assigned priority 6)
+};
+
+// --------------------------------------------------------------------------
+// Private functions
+// --------------------------------------------------------------------------
+
+FORCE_INLINE void Disable_Irq(IRQn_Type irq) {
+ NVIC_DisableIRQ(irq);
+
+ // We NEED memory barriers to ensure Interrupts are actually disabled!
+ // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
+ __DSB();
+ __ISB();
+}
+
+static bool tcIsSyncing(Tc * tc) {
+ return tc->COUNT32.STATUS.reg & TC_STATUS_SYNCBUSY;
+}
+
+static void tcReset( Tc * tc) {
+ tc->COUNT32.CTRLA.reg = TC_CTRLA_SWRST;
+ while (tcIsSyncing(tc)) {}
+ while (tc->COUNT32.CTRLA.bit.SWRST) {}
+}
+
+// --------------------------------------------------------------------------
+// Public functions
+// --------------------------------------------------------------------------
+
+void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) {
+ IRQn_Type irq = timer_config[timer_num].IRQ_Id;
+
+ // Disable interrupt, just in case it was already enabled
+ NVIC_DisableIRQ(irq);
+ NVIC_ClearPendingIRQ(irq);
+
+ if (timer_num == MF_TIMER_RTC) {
+
+ // https://github.com/arduino-libraries/RTCZero
+ Rtc * const rtc = timer_config[timer_num].pRtc;
+ PM->APBAMASK.reg |= PM_APBAMASK_RTC;
+
+ GCLK->CLKCTRL.reg = (uint32_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK4 | (RTC_GCLK_ID << GCLK_CLKCTRL_ID_Pos)));
+ while (GCLK->STATUS.bit.SYNCBUSY) {}
+
+ GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(4) | GCLK_GENCTRL_DIVSEL );
+ while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {}
+
+ GCLK->GENDIV.reg = GCLK_GENDIV_ID(4);
+ GCLK->GENDIV.bit.DIV=4;
+ while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {}
+
+ // Disable timer interrupt
+ rtc->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP0;
+ SYNC(rtc->MODE0.STATUS.bit.SYNCBUSY);
+
+ while(rtc->MODE0.STATUS.bit.SYNCBUSY) {}
+
+ // Stop timer, just in case, to be able to reconfigure it
+ rtc->MODE0.CTRL.reg =
+ RTC_MODE0_CTRL_MODE_COUNT32 | // Mode 0 = 32-bits counter
+ RTC_MODE0_CTRL_PRESCALER_DIV1024; // Divisor = 1024
+
+ while(rtc->MODE0.STATUS.bit.SYNCBUSY) {}
+
+ // Mode, reset counter on match
+ rtc->MODE0.CTRL.reg = RTC_MODE0_CTRL_MODE_COUNT32 | RTC_MODE0_CTRL_MATCHCLR;
+
+ // Set compare value
+ rtc->MODE0.COMP[0].reg = (32768 + frequency / 2) / frequency;
+ SYNC(rtc->MODE0.STATUS.bit.SYNCBUSY);
+
+ // Enable interrupt on compare
+ rtc->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0; // reset pending interrupt
+ rtc->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP0; // enable compare 0 interrupt
+
+ // And start timer
+ rtc->MODE0.CTRL.bit.ENABLE = true;
+ SYNC(rtc->MODE0.STATUS.bit.SYNCBUSY);
+
+ }
+ else if (timer_config[timer_num].type==TimerType::tcc) {
+
+ Tcc * const tc = timer_config[timer_num].pTcc;
+
+ PM->APBCMASK.reg |= PM_APBCMASK_TCC0;
+ GCLK->CLKCTRL.reg =(GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(TCC0_GCLK_ID));
+ SYNC (GCLK->STATUS.bit.SYNCBUSY);
+
+ tc->CTRLA.reg = TCC_CTRLA_SWRST;
+ SYNC (tc->SYNCBUSY.reg & TCC_SYNCBUSY_SWRST) {}
+
+ SYNC (tc->CTRLA.bit.SWRST);
+
+ tc->CTRLA.reg &= ~(TCC_CTRLA_ENABLE); // disable TC module
+
+ tc->CTRLA.reg |= TCC_WAVE_WAVEGEN_MFRQ;
+ tc->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV2;
+ tc->CC[0].reg = (HAL_TIMER_RATE) / frequency;
+ tc->INTENSET.reg = TCC_INTFLAG_MC0;
+ tc->CTRLA.reg |= TCC_CTRLA_ENABLE;
+ tc->INTFLAG.reg = 0xFF;
+ SYNC ( tc->STATUS.reg & TC_STATUS_SYNCBUSY);
+
+ }
+ else {
+ Tc * const tc = timer_config[timer_num].pTc;
+
+ // Disable timer interrupt
+ tc->COUNT32.INTENCLR.reg = TC_INTENCLR_OVF; // disable overflow interrupt
+
+ // TCn clock setup
+ GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)) ;
+ SYNC (GCLK->STATUS.bit.SYNCBUSY);
+
+ tcReset(tc); // reset TC
+
+ // Set Timer counter 5 Mode to 16 bits, it will become a 16bit counter ('mode1' in the datasheet)
+ tc->COUNT32.CTRLA.reg |= TC_CTRLA_MODE_COUNT32;
+ // Set TC waveform generation mode to 'match frequency'
+ tc->COUNT32.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
+ //set prescaler
+ //the clock normally counts at the GCLK_TC frequency, but we can set it to divide that frequency to slow it down
+ //you can use different prescaler divisons here like TC_CTRLA_PRESCALER_DIV1 to get a different range
+ tc->COUNT32.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1 | TC_CTRLA_ENABLE; //it will divide GCLK_TC frequency by 1024
+ //set the compare-capture register.
+ //The counter will count up to this value (it's a 16bit counter so we use uint16_t)
+ //this is how we fine-tune the frequency, make it count to a lower or higher value
+ //system clock should be 1MHz (8MHz/8) at Reset by default
+ tc->COUNT32.CC[0].reg = (uint16_t) (HAL_TIMER_RATE / frequency);
+ while (tcIsSyncing(tc)) {}
+
+ // Enable the TC interrupt request
+ tc->COUNT32.INTENSET.bit.MC0 = 1;
+ while (tcIsSyncing(tc)) {}
+ }
+
+ NVIC_SetPriority(irq, timer_config[timer_num].priority);
+ NVIC_EnableIRQ(irq);
+}
+
+void HAL_timer_enable_interrupt(const uint8_t timer_num) {
+ const IRQn_Type irq = timer_config[timer_num].IRQ_Id;
+ NVIC_EnableIRQ(irq);
+}
+
+void HAL_timer_disable_interrupt(const uint8_t timer_num) {
+ const IRQn_Type irq = timer_config[timer_num].IRQ_Id;
+ Disable_Irq(irq);
+}
+
+// missing from CMSIS: Check if interrupt is enabled or not
+static bool NVIC_GetEnabledIRQ(IRQn_Type IRQn) {
+ return TEST(NVIC->ISER[uint32_t(IRQn) >> 5], uint32_t(IRQn) & 0x1F);
+}
+
+bool HAL_timer_interrupt_enabled(const uint8_t timer_num) {
+ const IRQn_Type irq = timer_config[timer_num].IRQ_Id;
+ return NVIC_GetEnabledIRQ(irq);
+}
+
+#endif // __SAMD21__
diff --git a/Marlin/src/HAL/SAMD21/timers.h b/Marlin/src/HAL/SAMD21/timers.h
new file mode 100644
index 0000000000..303ccbdc50
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/timers.h
@@ -0,0 +1,160 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+
+#include
+
+// --------------------------------------------------------------------------
+// Defines
+// --------------------------------------------------------------------------
+
+typedef uint32_t hal_timer_t;
+#define HAL_TIMER_TYPE_MAX 0xFFFFFFFF
+
+#define HAL_TIMER_RATE F_CPU // frequency of timers peripherals
+
+#define MF_TIMER_RTC 8 // This is not a TC but a RTC
+
+#ifndef MF_TIMER_STEP
+ #define MF_TIMER_STEP 4 // Timer Index for Stepper
+#endif
+#ifndef MF_TIMER_PULSE
+ #define MF_TIMER_PULSE MF_TIMER_STEP
+#endif
+#ifndef MF_TIMER_TEMP
+ #define MF_TIMER_TEMP MF_TIMER_RTC // Timer Index for Temperature
+#endif
+
+#define TEMP_TIMER_FREQUENCY 1000 // temperature interrupt frequency
+
+#define STEPPER_TIMER_RATE HAL_TIMER_RATE // frequency of stepper timer (HAL_TIMER_RATE / STEPPER_TIMER_PRESCALE)
+#define STEPPER_TIMER_TICKS_PER_US (STEPPER_TIMER_RATE / 1000000) // stepper timer ticks per µs
+#define STEPPER_TIMER_PRESCALE (CYCLES_PER_MICROSECOND / STEPPER_TIMER_TICKS_PER_US)
+
+#define PULSE_TIMER_RATE STEPPER_TIMER_RATE
+#define PULSE_TIMER_PRESCALE STEPPER_TIMER_PRESCALE
+#define PULSE_TIMER_TICKS_PER_US STEPPER_TIMER_TICKS_PER_US
+
+#define ENABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_STEP)
+#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_STEP)
+#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(MF_TIMER_STEP)
+
+#define ENABLE_TEMPERATURE_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_TEMP)
+#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_TEMP)
+
+#define TC_PRIORITY(t) ( t == SERVO_TC ? 1 \
+ : (t == MF_TIMER_STEP || t == MF_TIMER_PULSE) ? 2 \
+ : (t == MF_TIMER_TEMP) ? 6 : 7 )
+
+#define _TC_HANDLER(t) void TC##t##_Handler()
+#define TC_HANDLER(t) _TC_HANDLER(t)
+#ifndef HAL_STEP_TIMER_ISR
+ #define HAL_STEP_TIMER_ISR() TC_HANDLER(MF_TIMER_STEP)
+#endif
+#if MF_TIMER_STEP != MF_TIMER_PULSE
+ #define HAL_PULSE_TIMER_ISR() TC_HANDLER(MF_TIMER_PULSE)
+#endif
+#if MF_TIMER_TEMP == MF_TIMER_RTC
+ #define HAL_TEMP_TIMER_ISR() void RTC_Handler()
+#else
+ #define HAL_TEMP_TIMER_ISR() TC_HANDLER(MF_TIMER_TEMP)
+#endif
+
+// --------------------------------------------------------------------------
+// Types
+// --------------------------------------------------------------------------
+typedef enum { tcc, tc, rtc } TimerType;
+
+typedef struct {
+ union {
+ Tc *pTc;
+ Tcc *pTcc;
+ Rtc *pRtc;
+ };
+ TimerType type;
+ IRQn_Type IRQ_Id;
+ uint8_t priority;
+} tTimerConfig;
+
+// --------------------------------------------------------------------------
+// Public Variables
+// --------------------------------------------------------------------------
+
+extern const tTimerConfig timer_config[];
+
+// --------------------------------------------------------------------------
+// Public functions
+// --------------------------------------------------------------------------
+
+void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency);
+
+FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, const hal_timer_t compare) {
+ // Should never be called with timer MF_TIMER_RTC
+ Tc * const tc = timer_config[timer_num].pTc;
+ tc->COUNT32.CC[0].reg = compare;
+}
+
+FORCE_INLINE static hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) {
+ // Should never be called with timer MF_TIMER_RTC
+ Tc * const tc = timer_config[timer_num].pTc;
+ return (hal_timer_t)tc->COUNT32.CC[0].reg;
+}
+
+FORCE_INLINE static hal_timer_t HAL_timer_get_count(const uint8_t timer_num) {
+ // Should never be called with timer MF_TIMER_RTC
+ Tc * const tc = timer_config[timer_num].pTc;
+ tc->COUNT32.READREQ.reg = TC_READREQ_RREQ;
+ // Request a read synchronization
+ SYNC (tc->COUNT32.STATUS.bit.SYNCBUSY);
+ //SYNC(tc->COUNT32.STATUS.bit.SYNCBUSY );
+ return tc->COUNT32.COUNT.reg;
+}
+
+void HAL_timer_enable_interrupt(const uint8_t timer_num);
+void HAL_timer_disable_interrupt(const uint8_t timer_num);
+bool HAL_timer_interrupt_enabled(const uint8_t timer_num);
+
+FORCE_INLINE static void HAL_timer_isr_prologue(const uint8_t timer_num) {
+ if (timer_num == MF_TIMER_RTC) {
+ Rtc * const rtc = timer_config[timer_num].pRtc;
+ // Clear interrupt flag
+ rtc->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0| RTC_MODE0_INTFLAG_OVF;
+
+ }
+ else if (timer_config[timer_num].type == TimerType::tcc){
+ Tcc * const tc = timer_config[timer_num].pTcc;
+ // Clear interrupt flag
+ tc->INTFLAG.reg = TCC_INTFLAG_OVF;
+ }
+ else {
+ Tc * const tc = timer_config[timer_num].pTc;
+ // Clear interrupt flag
+ tc->COUNT32.INTFLAG.bit.MC0 = 1;
+ }
+}
+
+#define HAL_timer_isr_epilogue(timer_num)
diff --git a/Marlin/src/HAL/SAMD21/u8g/LCD_I2C_routines.cpp b/Marlin/src/HAL/SAMD21/u8g/LCD_I2C_routines.cpp
new file mode 100644
index 0000000000..41da7c10fc
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/u8g/LCD_I2C_routines.cpp
@@ -0,0 +1,32 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+// adapted from I2C/master/master.c example
+// https://www-users.cs.york.ac.uk/~pcc/MCP/HAPR-Course-web/CMSIS/examples/html/master_8c_source.html
+
+#ifdef __SAMD21__
+
+#endif // __SAMD21__
diff --git a/Marlin/src/HAL/SAMD21/u8g/LCD_I2C_routines.h b/Marlin/src/HAL/SAMD21/u8g/LCD_I2C_routines.h
new file mode 100644
index 0000000000..d6a3c4fe0b
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/u8g/LCD_I2C_routines.h
@@ -0,0 +1,27 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+#pragma once
diff --git a/Marlin/src/HAL/SAMD21/u8g/LCD_defines.h b/Marlin/src/HAL/SAMD21/u8g/LCD_defines.h
new file mode 100644
index 0000000000..fa98725d22
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/u8g/LCD_defines.h
@@ -0,0 +1,41 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+#pragma once
+
+/**
+ * SAMD21 LCD-specific defines
+ */
+
+// The following are optional depending on the platform.
+
+// definitions of HAL specific com and device drivers.
+uint8_t u8g_com_samd21_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);
+uint8_t u8g_com_samd21_st7920_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);
+
+// connect U8g com generic com names to the desired driver
+#define U8G_COM_HW_SPI u8g_com_samd21_st7920_hw_spi_fn // use SAMD21 specific hardware SPI routine
+#define U8G_COM_ST7920_HW_SPI u8g_com_samd21_st7920_hw_spi_fn
diff --git a/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.c b/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.c
new file mode 100644
index 0000000000..f9f77825f6
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.c
@@ -0,0 +1,42 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+
+/**
+ * Low level pin manipulation routines - used by all the drivers.
+ *
+ * These are based on the SAMD51 pinMode, digitalRead & digitalWrite routines.
+ *
+ * Couldn't just call exact copies because the overhead killed the LCD update speed
+ * With an intermediate level the softspi was running in the 10-20kHz range which
+ * resulted in using about about 25% of the CPU's time.
+ */
+
+#ifdef __SAMD21__
+
+#include
+
+#endif // __SAMD21__
diff --git a/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.h b/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.h
new file mode 100644
index 0000000000..92626552b0
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.h
@@ -0,0 +1,42 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+#pragma once
+
+/**
+ * Low level pin manipulation routines - used by all the drivers.
+ *
+ * These are based on the SAMD51 pinMode, digitalRead & digitalWrite routines.
+ *
+ * Couldn't just call exact copies because the overhead killed the LCD update speed
+ * With an intermediate level the softspi was running in the 10-20kHz range which
+ * resulted in using about about 25% of the CPU's time.
+ */
+
+void u8g_SetPinOutput(uint8_t internal_pin_number);
+void u8g_SetPinInput(uint8_t internal_pin_number);
+void u8g_SetPinLevel(uint8_t pin, uint8_t pin_status);
+uint8_t u8g_GetPinLevel(uint8_t pin);
diff --git a/Marlin/src/HAL/SAMD21/u8g/u8g_com_HAL_samd21_shared_hw_spi.cpp b/Marlin/src/HAL/SAMD21/u8g/u8g_com_HAL_samd21_shared_hw_spi.cpp
new file mode 100644
index 0000000000..02dc772296
--- /dev/null
+++ b/Marlin/src/HAL/SAMD21/u8g/u8g_com_HAL_samd21_shared_hw_spi.cpp
@@ -0,0 +1,154 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * SAMD21 HAL developed by Bart Meijer (brupje)
+ * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
+ */
+
+/**
+ * Based on u8g_com_msp430_hw_spi.c
+ *
+ * Universal 8bit Graphics Library
+ *
+ * Copyright (c) 2012, 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.
+ */
+
+#ifdef __SAMD21__
+
+#include
+#include "SPI.h"
+
+#include "../../shared/HAL_SPI.h"
+
+#ifndef LCD_SPI_SPEED
+ #define LCD_SPI_SPEED SPI_QUARTER_SPEED
+#endif
+
+void u8g_SetPIOutput(u8g_t *u8g, uint8_t pin_index) {
+ if (u8g->pin_list[pin_index]!= U8G_PIN_NONE)
+ pinMode(u8g->pin_list[pin_index],OUTPUT);
+}
+
+void u8g_SetPILevel(u8g_t *u8g, uint8_t pin_index, uint8_t level) {
+ if (u8g->pin_list[pin_index]!= U8G_PIN_NONE)
+ digitalWrite(u8g->pin_list[pin_index],level);
+}
+
+uint8_t u8g_com_samd21_st7920_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) {
+
+ static SPISettings lcdSPIConfig;
+
+ switch (msg) {
+ case U8G_COM_MSG_STOP:
+ break;
+
+ case U8G_COM_MSG_INIT:
+ u8g_SetPIOutput(u8g, U8G_PI_CS);
+ u8g_SetPIOutput(u8g, U8G_PI_A0);
+ u8g_SetPIOutput(u8g, U8G_PI_RESET);
+
+ u8g_SetPILevel(u8g, U8G_PI_CS, LOW);
+
+ spiBegin();
+ lcdSPIConfig = SPISettings(900000, MSBFIRST, SPI_MODE0);
+ u8g->pin_list[U8G_PI_A0_STATE] = 0;
+ break;
+
+ case U8G_COM_MSG_ADDRESS: // define cmd (arg_val = 0) or data mode (arg_val = 1)
+ u8g_SetPILevel(u8g, U8G_PI_A0, arg_val);
+ u8g->pin_list[U8G_PI_A0_STATE] = arg_val;
+ break;
+
+ case U8G_COM_MSG_CHIP_SELECT: // arg_val == 1 means chip selected, but ST7920 is active high, so needs inverting
+ u8g_SetPILevel(u8g, U8G_PI_CS, arg_val ? HIGH : LOW);
+ break;
+
+ case U8G_COM_MSG_RESET:
+ u8g_SetPILevel(u8g, U8G_PI_RESET, arg_val);
+ break;
+
+ case U8G_COM_MSG_WRITE_BYTE:
+ SPI.beginTransaction(lcdSPIConfig);
+
+ if (u8g->pin_list[U8G_PI_A0_STATE] == 0) { // command
+ SPI.transfer(0x0f8); u8g->pin_list[U8G_PI_A0_STATE] = 2;
+ }
+ else if (u8g->pin_list[U8G_PI_A0_STATE] == 1) { // data
+ SPI.transfer(0x0fa); u8g->pin_list[U8G_PI_A0_STATE] = 2;
+ }
+
+ SPI.transfer(arg_val & 0x0f0);
+ SPI.transfer(arg_val << 4);
+ SPI.endTransaction();
+ break;
+
+ case U8G_COM_MSG_WRITE_SEQ:
+ SPI.beginTransaction(lcdSPIConfig);
+
+ if (u8g->pin_list[U8G_PI_A0_STATE] == 0 ) { // command
+ SPI.transfer(0x0f8); u8g->pin_list[U8G_PI_A0_STATE] = 2;
+ }
+ else if (u8g->pin_list[U8G_PI_A0_STATE] == 1) { // data
+ SPI.transfer(0x0fa); u8g->pin_list[U8G_PI_A0_STATE] = 2;
+ }
+
+ uint8_t *ptr = (uint8_t*)arg_ptr;
+ while (arg_val > 0) {
+ SPI.transfer((*ptr) & 0x0f0);
+ SPI.transfer((*ptr) << 4);
+ ptr++;
+ arg_val--;
+ }
+
+ SPI.endTransaction();
+ break;
+ }
+ return 1;
+}
+
+#endif // __SAMD21__
diff --git a/Marlin/src/HAL/STM32/HAL.cpp b/Marlin/src/HAL/STM32/HAL.cpp
new file mode 100644
index 0000000000..aff52f597f
--- /dev/null
+++ b/Marlin/src/HAL/STM32/HAL.cpp
@@ -0,0 +1,182 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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 "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfig.h"
+#include "../shared/Delay.h"
+
+#include "usb_serial.h"
+
+#ifdef USBCON
+ DefaultSerial1 MSerialUSB(false, SerialUSB);
+#endif
+
+#if ENABLED(SRAM_EEPROM_EMULATION)
+ #if STM32F7xx
+ #include
+ #elif STM32F4xx
+ #include
+ #else
+ #error "SRAM_EEPROM_EMULATION is currently only supported for STM32F4xx and STM32F7xx"
+ #endif
+#endif
+
+#if HAS_SD_HOST_DRIVE
+ #include "msc_sd.h"
+ #include "usbd_cdc_if.h"
+#endif
+
+// ------------------------
+// Public Variables
+// ------------------------
+
+uint16_t MarlinHAL::adc_result;
+
+// ------------------------
+// Public functions
+// ------------------------
+
+#if ENABLED(POSTMORTEM_DEBUGGING)
+ extern void install_min_serial();
+#endif
+
+// HAL initialization task
+void MarlinHAL::init() {
+ // Ensure F_CPU is a constant expression.
+ // If the compiler breaks here, it means that delay code that should compute at compile time will not work.
+ // So better safe than sorry here.
+ constexpr int cpuFreq = F_CPU;
+ UNUSED(cpuFreq);
+
+ #if ENABLED(SDSUPPORT) && DISABLED(SDIO_SUPPORT) && (defined(SDSS) && SDSS != -1)
+ OUT_WRITE(SDSS, HIGH); // Try to set SDSS inactive before any other SPI users start up
+ #endif
+
+ #if PIN_EXISTS(LED)
+ OUT_WRITE(LED_PIN, LOW);
+ #endif
+
+ #if ENABLED(SRAM_EEPROM_EMULATION)
+ __HAL_RCC_PWR_CLK_ENABLE();
+ HAL_PWR_EnableBkUpAccess(); // Enable access to backup SRAM
+ __HAL_RCC_BKPSRAM_CLK_ENABLE();
+ LL_PWR_EnableBkUpRegulator(); // Enable backup regulator
+ while (!LL_PWR_IsActiveFlag_BRR()); // Wait until backup regulator is initialized
+ #endif
+
+ SetTimerInterruptPriorities();
+
+ #if ENABLED(EMERGENCY_PARSER) && (USBD_USE_CDC || USBD_USE_CDC_MSC)
+ USB_Hook_init();
+ #endif
+
+ TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); // Install the min serial handler
+
+ TERN_(HAS_SD_HOST_DRIVE, MSC_SD_init()); // Enable USB SD card access
+
+ #if PIN_EXISTS(USB_CONNECT)
+ OUT_WRITE(USB_CONNECT_PIN, !USB_CONNECT_INVERTING); // USB clear connection
+ delay(1000); // Give OS time to notice
+ WRITE(USB_CONNECT_PIN, USB_CONNECT_INVERTING);
+ #endif
+}
+
+// HAL idle task
+void MarlinHAL::idletask() {
+ #if HAS_SHARED_MEDIA
+ // Stm32duino currently doesn't have a "loop/idle" method
+ CDC_resume_receive();
+ CDC_continue_transmit();
+ #endif
+}
+
+void MarlinHAL::reboot() { NVIC_SystemReset(); }
+
+uint8_t MarlinHAL::get_reset_source() {
+ return
+ #ifdef RCC_FLAG_IWDGRST // Some sources may not exist...
+ RESET != __HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) ? RST_WATCHDOG :
+ #endif
+ #ifdef RCC_FLAG_IWDG1RST
+ RESET != __HAL_RCC_GET_FLAG(RCC_FLAG_IWDG1RST) ? RST_WATCHDOG :
+ #endif
+ #ifdef RCC_FLAG_IWDG2RST
+ RESET != __HAL_RCC_GET_FLAG(RCC_FLAG_IWDG2RST) ? RST_WATCHDOG :
+ #endif
+ #ifdef RCC_FLAG_SFTRST
+ RESET != __HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST) ? RST_SOFTWARE :
+ #endif
+ #ifdef RCC_FLAG_PINRST
+ RESET != __HAL_RCC_GET_FLAG(RCC_FLAG_PINRST) ? RST_EXTERNAL :
+ #endif
+ #ifdef RCC_FLAG_PORRST
+ RESET != __HAL_RCC_GET_FLAG(RCC_FLAG_PORRST) ? RST_POWER_ON :
+ #endif
+ 0
+ ;
+}
+
+void MarlinHAL::clear_reset_source() { __HAL_RCC_CLEAR_RESET_FLAGS(); }
+
+// ------------------------
+// Watchdog Timer
+// ------------------------
+
+#if ENABLED(USE_WATCHDOG)
+
+ #define WDT_TIMEOUT_US TERN(WATCHDOG_DURATION_8S, 8000000, 4000000) // 4 or 8 second timeout
+
+ #include
+
+ void MarlinHAL::watchdog_init() {
+ IF_DISABLED(DISABLE_WATCHDOG_INIT, IWatchdog.begin(WDT_TIMEOUT_US));
+ }
+
+ void MarlinHAL::watchdog_refresh() {
+ IWatchdog.reload();
+ #if DISABLED(PINS_DEBUGGING) && PIN_EXISTS(LED)
+ TOGGLE(LED_PIN); // heartbeat indicator
+ #endif
+ }
+
+#endif
+
+extern "C" {
+ extern unsigned int _ebss; // end of bss section
+}
+
+// Reset the system to initiate a firmware flash
+WEAK void flashFirmware(const int16_t) { hal.reboot(); }
+
+// Maple Compatibility
+volatile uint32_t systick_uptime_millis = 0;
+systickCallback_t systick_user_callback;
+void systick_attach_callback(systickCallback_t cb) { systick_user_callback = cb; }
+void HAL_SYSTICK_Callback() {
+ systick_uptime_millis++;
+ if (systick_user_callback) systick_user_callback();
+}
+
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/HAL.h b/Marlin/src/HAL/STM32/HAL.h
new file mode 100644
index 0000000000..3e85aca293
--- /dev/null
+++ b/Marlin/src/HAL/STM32/HAL.h
@@ -0,0 +1,281 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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
+
+#define CPU_32_BIT
+
+#include "../../core/macros.h"
+#include "../shared/Marduino.h"
+#include "../shared/math_32bit.h"
+#include "../shared/HAL_SPI.h"
+#include "fastio.h"
+#include "Servo.h"
+#include "MarlinSerial.h"
+
+#include "../../inc/MarlinConfigPre.h"
+
+#include
+
+//
+// Default graphical display delays
+//
+#define CPU_ST7920_DELAY_1 300
+#define CPU_ST7920_DELAY_2 40
+#define CPU_ST7920_DELAY_3 340
+
+// ------------------------
+// Serial ports
+// ------------------------
+#ifdef USBCON
+ #include
+ #include "../../core/serial_hook.h"
+ typedef ForwardSerial1Class< decltype(SerialUSB) > DefaultSerial1;
+ extern DefaultSerial1 MSerialUSB;
+#endif
+
+#define _MSERIAL(X) MSerial##X
+#define MSERIAL(X) _MSERIAL(X)
+
+#if WITHIN(SERIAL_PORT, 1, 6)
+ #define MYSERIAL1 MSERIAL(SERIAL_PORT)
+#elif !defined(USBCON)
+ #error "SERIAL_PORT must be from 1 to 6."
+#elif SERIAL_PORT == -1
+ #define MYSERIAL1 MSerialUSB
+#else
+ #error "SERIAL_PORT must be from 1 to 6, or -1 for Native USB."
+#endif
+
+#ifdef SERIAL_PORT_2
+ #if WITHIN(SERIAL_PORT_2, 1, 6)
+ #define MYSERIAL2 MSERIAL(SERIAL_PORT_2)
+ #elif !defined(USBCON)
+ #error "SERIAL_PORT must be from 1 to 6."
+ #elif SERIAL_PORT_2 == -1
+ #define MYSERIAL2 MSerialUSB
+ #else
+ #error "SERIAL_PORT_2 must be from 1 to 6, or -1 for Native USB."
+ #endif
+#endif
+
+#ifdef SERIAL_PORT_3
+ #if WITHIN(SERIAL_PORT_3, 1, 6)
+ #define MYSERIAL3 MSERIAL(SERIAL_PORT_3)
+ #elif !defined(USBCON)
+ #error "SERIAL_PORT must be from 1 to 6."
+ #elif SERIAL_PORT_3 == -1
+ #define MYSERIAL3 MSerialUSB
+ #else
+ #error "SERIAL_PORT_3 must be from 1 to 6, or -1 for Native USB."
+ #endif
+#endif
+
+#ifdef MMU2_SERIAL_PORT
+ #if WITHIN(MMU2_SERIAL_PORT, 1, 6)
+ #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT)
+ #elif !defined(USBCON)
+ #error "SERIAL_PORT must be from 1 to 6."
+ #elif MMU2_SERIAL_PORT == -1
+ #define MMU2_SERIAL MSerialUSB
+ #else
+ #error "MMU2_SERIAL_PORT must be from 1 to 6, or -1 for Native USB."
+ #endif
+#endif
+
+#ifdef LCD_SERIAL_PORT
+ #if WITHIN(LCD_SERIAL_PORT, 1, 6)
+ #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT)
+ #elif !defined(USBCON)
+ #error "SERIAL_PORT must be from 1 to 6."
+ #elif LCD_SERIAL_PORT == -1
+ #define LCD_SERIAL MSerialUSB
+ #else
+ #error "LCD_SERIAL_PORT must be from 1 to 6, or -1 for Native USB."
+ #endif
+ #if HAS_DGUS_LCD
+ #define SERIAL_GET_TX_BUFFER_FREE() LCD_SERIAL.availableForWrite()
+ #endif
+#endif
+
+/**
+ * TODO: review this to return 1 for pins that are not analog input
+ */
+#ifndef analogInputToDigitalPin
+ #define analogInputToDigitalPin(p) (p)
+#endif
+
+//
+// Interrupts
+//
+#define CRITICAL_SECTION_START() const bool irqon = !__get_PRIMASK(); __disable_irq()
+#define CRITICAL_SECTION_END() if (irqon) __enable_irq()
+#define cli() __disable_irq()
+#define sei() __enable_irq()
+
+// ------------------------
+// Types
+// ------------------------
+
+typedef double isr_float_t; // FPU ops are used for single-precision, so use double for ISRs.
+
+#ifdef STM32G0B1xx
+ typedef int32_t pin_t;
+#else
+ typedef int16_t pin_t;
+#endif
+
+class libServo;
+typedef libServo hal_servo_t;
+#define PAUSE_SERVO_OUTPUT() libServo::pause_all_servos()
+#define RESUME_SERVO_OUTPUT() libServo::resume_all_servos()
+
+// ------------------------
+// ADC
+// ------------------------
+
+#ifdef ADC_RESOLUTION
+ #define HAL_ADC_RESOLUTION ADC_RESOLUTION
+#else
+ #define HAL_ADC_RESOLUTION 12
+#endif
+
+#define HAL_ADC_VREF 3.3
+
+//
+// Pin Mapping for M42, M43, M226
+//
+#define GET_PIN_MAP_PIN(index) index
+#define GET_PIN_MAP_INDEX(pin) pin
+#define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval)
+
+#ifdef STM32F1xx
+ #define JTAG_DISABLE() AFIO_DBGAFR_CONFIG(AFIO_MAPR_SWJ_CFG_JTAGDISABLE)
+ #define JTAGSWD_DISABLE() AFIO_DBGAFR_CONFIG(AFIO_MAPR_SWJ_CFG_DISABLE)
+ #define JTAGSWD_RESET() AFIO_DBGAFR_CONFIG(AFIO_MAPR_SWJ_CFG_RESET); // Reset: FULL SWD+JTAG
+#endif
+
+#define PLATFORM_M997_SUPPORT
+void flashFirmware(const int16_t);
+
+// Maple Compatibility
+typedef void (*systickCallback_t)(void);
+void systick_attach_callback(systickCallback_t cb);
+void HAL_SYSTICK_Callback();
+
+extern volatile uint32_t systick_uptime_millis;
+
+#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment
+
+// ------------------------
+// Class Utilities
+// ------------------------
+
+// Memory related
+#define __bss_end __bss_end__
+
+extern "C" char* _sbrk(int incr);
+
+#pragma GCC diagnostic push
+#if GCC_VERSION <= 50000
+ #pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+static inline int freeMemory() {
+ volatile char top;
+ return &top - reinterpret_cast(_sbrk(0));
+}
+
+#pragma GCC diagnostic pop
+
+// ------------------------
+// MarlinHAL Class
+// ------------------------
+
+class MarlinHAL {
+public:
+
+ // Earliest possible init, before setup()
+ MarlinHAL() {}
+
+ // Watchdog
+ static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {});
+ static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {});
+
+ static void init(); // Called early in setup()
+ static void init_board() {} // Called less early in setup()
+ static void reboot(); // Restart the firmware from 0x0
+
+ // Interrupts
+ static bool isr_state() { return !__get_PRIMASK(); }
+ static void isr_on() { sei(); }
+ static void isr_off() { cli(); }
+
+ static void delay_ms(const int ms) { delay(ms); }
+
+ // Tasks, called from idle()
+ static void idletask();
+
+ // Reset
+ static uint8_t get_reset_source();
+ static void clear_reset_source();
+
+ // Free SRAM
+ static int freeMemory() { return ::freeMemory(); }
+
+ //
+ // ADC Methods
+ //
+
+ static uint16_t adc_result;
+
+ // Called by Temperature::init once at startup
+ static void adc_init() {
+ analogReadResolution(HAL_ADC_RESOLUTION);
+ }
+
+ // Called by Temperature::init for each sensor at startup
+ static void adc_enable(const pin_t pin) { pinMode(pin, INPUT); }
+
+ // Begin ADC sampling on the given pin. Called from Temperature::isr!
+ static void adc_start(const pin_t pin) { adc_result = analogRead(pin); }
+
+ // Is the ADC ready for reading?
+ static bool adc_ready() { return true; }
+
+ // The current value of the ADC register
+ static uint16_t adc_value() { return adc_result; }
+
+ /**
+ * Set the PWM duty cycle for the pin to the given value.
+ * Optionally invert the duty cycle [default = false]
+ * Optionally change the maximum size of the provided value to enable finer PWM duty control [default = 255]
+ */
+ static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false);
+
+ /**
+ * Set the frequency of the timer for the given pin.
+ * All Timer PWM pins run at the same frequency.
+ */
+ static void set_pwm_frequency(const pin_t pin, const uint16_t f_desired);
+
+};
diff --git a/Marlin/src/HAL/STM32/HAL_SPI.cpp b/Marlin/src/HAL/STM32/HAL_SPI.cpp
new file mode 100644
index 0000000000..40d320d5e8
--- /dev/null
+++ b/Marlin/src/HAL/STM32/HAL_SPI.cpp
@@ -0,0 +1,231 @@
+/**
+ * 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
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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 "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfig.h"
+
+#include
+
+// ------------------------
+// Public Variables
+// ------------------------
+
+static SPISettings spiConfig;
+
+// ------------------------
+// Public functions
+// ------------------------
+
+#if ENABLED(SOFTWARE_SPI)
+
+ // ------------------------
+ // Software SPI
+ // ------------------------
+
+ #include "../shared/Delay.h"
+
+ void spiBegin(void) {
+ #if PIN_EXISTS(SD_SS)
+ OUT_WRITE(SD_SS_PIN, HIGH);
+ #endif
+ OUT_WRITE(SD_SCK_PIN, HIGH);
+ SET_INPUT(SD_MISO_PIN);
+ OUT_WRITE(SD_MOSI_PIN, HIGH);
+ }
+
+ // Use function with compile-time value so we can actually reach the desired frequency
+ // Need to adjust this a little bit: on a 72MHz clock, we have 14ns/clock
+ // and we'll use ~3 cycles to jump to the method and going back, so it'll take ~40ns from the given clock here
+ #define CALLING_COST_NS (3U * 1000000000U) / (F_CPU)
+ void (*delaySPIFunc)();
+ void delaySPI_125() { DELAY_NS(125 - CALLING_COST_NS); }
+ void delaySPI_250() { DELAY_NS(250 - CALLING_COST_NS); }
+ void delaySPI_500() { DELAY_NS(500 - CALLING_COST_NS); }
+ void delaySPI_1000() { DELAY_NS(1000 - CALLING_COST_NS); }
+ void delaySPI_2000() { DELAY_NS(2000 - CALLING_COST_NS); }
+ void delaySPI_4000() { DELAY_NS(4000 - CALLING_COST_NS); }
+
+ void spiInit(uint8_t spiRate) {
+ // Use datarates Marlin uses
+ switch (spiRate) {
+ case SPI_FULL_SPEED: delaySPIFunc = &delaySPI_125; break; // desired: 8,000,000 actual: ~1.1M
+ case SPI_HALF_SPEED: delaySPIFunc = &delaySPI_125; break; // desired: 4,000,000 actual: ~1.1M
+ case SPI_QUARTER_SPEED:delaySPIFunc = &delaySPI_250; break; // desired: 2,000,000 actual: ~890K
+ case SPI_EIGHTH_SPEED: delaySPIFunc = &delaySPI_500; break; // desired: 1,000,000 actual: ~590K
+ case SPI_SPEED_5: delaySPIFunc = &delaySPI_1000; break; // desired: 500,000 actual: ~360K
+ case SPI_SPEED_6: delaySPIFunc = &delaySPI_2000; break; // desired: 250,000 actual: ~210K
+ default: delaySPIFunc = &delaySPI_4000; break; // desired: 125,000 actual: ~123K
+ }
+ SPI.begin();
+ }
+
+ // Begin SPI transaction, set clock, bit order, data mode
+ void spiBeginTransaction(uint32_t spiClock, uint8_t bitOrder, uint8_t dataMode) { /* do nothing */ }
+
+ uint8_t HAL_SPI_STM32_SpiTransfer_Mode_3(uint8_t b) { // using Mode 3
+ for (uint8_t bits = 8; bits--;) {
+ WRITE(SD_SCK_PIN, LOW);
+ WRITE(SD_MOSI_PIN, b & 0x80);
+
+ delaySPIFunc();
+ WRITE(SD_SCK_PIN, HIGH);
+ delaySPIFunc();
+
+ b <<= 1; // little setup time
+ b |= (READ(SD_MISO_PIN) != 0);
+ }
+ DELAY_NS(125);
+ return b;
+ }
+
+ // Soft SPI receive byte
+ uint8_t spiRec() {
+ hal.isr_off(); // No interrupts during byte receive
+ const uint8_t data = HAL_SPI_STM32_SpiTransfer_Mode_3(0xFF);
+ hal.isr_on(); // Enable interrupts
+ return data;
+ }
+
+ // Soft SPI read data
+ void spiRead(uint8_t *buf, uint16_t nbyte) {
+ for (uint16_t i = 0; i < nbyte; i++)
+ buf[i] = spiRec();
+ }
+
+ // Soft SPI send byte
+ void spiSend(uint8_t data) {
+ hal.isr_off(); // No interrupts during byte send
+ HAL_SPI_STM32_SpiTransfer_Mode_3(data); // Don't care what is received
+ hal.isr_on(); // Enable interrupts
+ }
+
+ // Soft SPI send block
+ void spiSendBlock(uint8_t token, const uint8_t *buf) {
+ spiSend(token);
+ for (uint16_t i = 0; i < 512; i++)
+ spiSend(buf[i]);
+ }
+
+#else
+
+ // ------------------------
+ // Hardware SPI
+ // ------------------------
+
+ /**
+ * VGPV SPI speed start and PCLK2/2, by default 108/2 = 54Mhz
+ */
+
+ /**
+ * @brief Begin SPI port setup
+ *
+ * @return Nothing
+ *
+ * @details Only configures SS pin since stm32duino creates and initialize the SPI object
+ */
+ void spiBegin() {
+ #if PIN_EXISTS(SD_SS)
+ OUT_WRITE(SD_SS_PIN, HIGH);
+ #endif
+ }
+
+ // Configure SPI for specified SPI speed
+ void spiInit(uint8_t spiRate) {
+ // Use datarates Marlin uses
+ uint32_t clock;
+ switch (spiRate) {
+ case SPI_FULL_SPEED: clock = 20000000; break; // 13.9mhz=20000000 6.75mhz=10000000 3.38mhz=5000000 .833mhz=1000000
+ case SPI_HALF_SPEED: clock = 5000000; break;
+ case SPI_QUARTER_SPEED: clock = 2500000; break;
+ case SPI_EIGHTH_SPEED: clock = 1250000; break;
+ case SPI_SPEED_5: clock = 625000; break;
+ case SPI_SPEED_6: clock = 300000; break;
+ default:
+ clock = 4000000; // Default from the SPI library
+ }
+ spiConfig = SPISettings(clock, MSBFIRST, SPI_MODE0);
+
+ SPI.setMISO(SD_MISO_PIN);
+ SPI.setMOSI(SD_MOSI_PIN);
+ SPI.setSCLK(SD_SCK_PIN);
+
+ SPI.begin();
+ }
+
+ /**
+ * @brief Receives a single byte from the SPI port.
+ *
+ * @return Byte received
+ *
+ * @details
+ */
+ uint8_t spiRec() {
+ uint8_t returnByte = SPI.transfer(0xFF);
+ return returnByte;
+ }
+
+ /**
+ * @brief Receive a number of bytes from the SPI port to a buffer
+ *
+ * @param buf Pointer to starting address of buffer to write to.
+ * @param nbyte Number of bytes to receive.
+ * @return Nothing
+ *
+ * @details Uses DMA
+ */
+ void spiRead(uint8_t *buf, uint16_t nbyte) {
+ if (nbyte == 0) return;
+ memset(buf, 0xFF, nbyte);
+ SPI.transfer(buf, nbyte);
+ }
+
+ /**
+ * @brief Send a single byte on SPI port
+ *
+ * @param b Byte to send
+ *
+ * @details
+ */
+ void spiSend(uint8_t b) {
+ SPI.transfer(b);
+ }
+
+ /**
+ * @brief Write token and then write from 512 byte buffer to SPI (for SD card)
+ *
+ * @param buf Pointer with buffer start address
+ * @return Nothing
+ *
+ * @details Use DMA
+ */
+ void spiSendBlock(uint8_t token, const uint8_t *buf) {
+ uint8_t rxBuf[512];
+ SPI.transfer(token);
+ SPI.transfer((uint8_t*)buf, &rxBuf, 512);
+ }
+
+#endif // SOFTWARE_SPI
+
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/MarlinSPI.cpp b/Marlin/src/HAL/STM32/MarlinSPI.cpp
new file mode 100644
index 0000000000..f7c603d77e
--- /dev/null
+++ b/Marlin/src/HAL/STM32/MarlinSPI.cpp
@@ -0,0 +1,174 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../platforms.h"
+
+#if defined(HAL_STM32) && !defined(STM32H7xx)
+
+#include "MarlinSPI.h"
+
+static void spi_init(spi_t *obj, uint32_t speed, spi_mode_e mode, uint8_t msb, uint32_t dataSize) {
+ spi_init(obj, speed, mode, msb);
+ // spi_init set 8bit always
+ // TODO: copy the code from spi_init and handle data size, to avoid double init always!!
+ if (dataSize != SPI_DATASIZE_8BIT) {
+ obj->handle.Init.DataSize = dataSize;
+ HAL_SPI_Init(&obj->handle);
+ __HAL_SPI_ENABLE(&obj->handle);
+ }
+}
+
+void MarlinSPI::setClockDivider(uint8_t _div) {
+ _speed = spi_getClkFreq(&_spi);// / _div;
+ _clockDivider = _div;
+}
+
+void MarlinSPI::begin(void) {
+ //TODO: only call spi_init if any parameter changed!!
+ spi_init(&_spi, _speed, _dataMode, _bitOrder, _dataSize);
+}
+
+void MarlinSPI::setupDma(SPI_HandleTypeDef &_spiHandle, DMA_HandleTypeDef &_dmaHandle, uint32_t direction, bool minc) {
+ _dmaHandle.Init.Direction = direction;
+ _dmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;
+ _dmaHandle.Init.Mode = DMA_NORMAL;
+ _dmaHandle.Init.Priority = DMA_PRIORITY_LOW;
+ _dmaHandle.Init.MemInc = minc ? DMA_MINC_ENABLE : DMA_MINC_DISABLE;
+
+ if (_dataSize == DATA_SIZE_8BIT) {
+ _dmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
+ _dmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
+ }
+ else {
+ _dmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
+ _dmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
+ }
+ #ifdef STM32F4xx
+ _dmaHandle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
+ #endif
+
+ // start DMA hardware
+ // TODO: check if hardware is already enabled
+ #ifdef SPI1_BASE
+ if (_spiHandle.Instance == SPI1) {
+ #ifdef STM32F1xx
+ __HAL_RCC_DMA1_CLK_ENABLE();
+ _dmaHandle.Instance = (direction == DMA_MEMORY_TO_PERIPH) ? DMA1_Channel3 : DMA1_Channel2;
+ #elif defined(STM32F4xx)
+ __HAL_RCC_DMA2_CLK_ENABLE();
+ _dmaHandle.Init.Channel = DMA_CHANNEL_3;
+ _dmaHandle.Instance = (direction == DMA_MEMORY_TO_PERIPH) ? DMA2_Stream3 : DMA2_Stream0;
+ #endif
+ }
+ #endif
+ #ifdef SPI2_BASE
+ if (_spiHandle.Instance == SPI2) {
+ #ifdef STM32F1xx
+ __HAL_RCC_DMA1_CLK_ENABLE();
+ _dmaHandle.Instance = (direction == DMA_MEMORY_TO_PERIPH) ? DMA1_Channel5 : DMA1_Channel4;
+ #elif defined(STM32F4xx)
+ __HAL_RCC_DMA1_CLK_ENABLE();
+ _dmaHandle.Init.Channel = DMA_CHANNEL_0;
+ _dmaHandle.Instance = (direction == DMA_MEMORY_TO_PERIPH) ? DMA1_Stream4 : DMA1_Stream3;
+ #endif
+ }
+ #endif
+ #ifdef SPI3_BASE
+ if (_spiHandle.Instance == SPI3) {
+ #ifdef STM32F1xx
+ __HAL_RCC_DMA2_CLK_ENABLE();
+ _dmaHandle.Instance = (direction == DMA_MEMORY_TO_PERIPH) ? DMA2_Channel2 : DMA2_Channel1;
+ #elif defined(STM32F4xx)
+ __HAL_RCC_DMA1_CLK_ENABLE();
+ _dmaHandle.Init.Channel = DMA_CHANNEL_0;
+ _dmaHandle.Instance = (direction == DMA_MEMORY_TO_PERIPH) ? DMA1_Stream5 : DMA1_Stream2;
+ #endif
+ }
+ #endif
+
+ HAL_DMA_Init(&_dmaHandle);
+}
+
+byte MarlinSPI::transfer(uint8_t _data) {
+ uint8_t rxData = 0xFF;
+ HAL_SPI_TransmitReceive(&_spi.handle, &_data, &rxData, 1, HAL_MAX_DELAY);
+ return rxData;
+}
+
+__STATIC_INLINE void LL_SPI_EnableDMAReq_RX(SPI_TypeDef *SPIx) { SET_BIT(SPIx->CR2, SPI_CR2_RXDMAEN); }
+__STATIC_INLINE void LL_SPI_EnableDMAReq_TX(SPI_TypeDef *SPIx) { SET_BIT(SPIx->CR2, SPI_CR2_TXDMAEN); }
+
+uint8_t MarlinSPI::dmaTransfer(const void *transmitBuf, void *receiveBuf, uint16_t length) {
+ const uint8_t ff = 0xFF;
+
+ //if (!LL_SPI_IsEnabled(_spi.handle)) // only enable if disabled
+ __HAL_SPI_ENABLE(&_spi.handle);
+
+ if (receiveBuf) {
+ setupDma(_spi.handle, _dmaRx, DMA_PERIPH_TO_MEMORY, true);
+ HAL_DMA_Start(&_dmaRx, (uint32_t)&(_spi.handle.Instance->DR), (uint32_t)receiveBuf, length);
+ LL_SPI_EnableDMAReq_RX(_spi.handle.Instance); // Enable Rx DMA Request
+ }
+
+ // check for 2 lines transfer
+ bool mincTransmit = true;
+ if (transmitBuf == nullptr && _spi.handle.Init.Direction == SPI_DIRECTION_2LINES && _spi.handle.Init.Mode == SPI_MODE_MASTER) {
+ transmitBuf = &ff;
+ mincTransmit = false;
+ }
+
+ if (transmitBuf) {
+ setupDma(_spi.handle, _dmaTx, DMA_MEMORY_TO_PERIPH, mincTransmit);
+ HAL_DMA_Start(&_dmaTx, (uint32_t)transmitBuf, (uint32_t)&(_spi.handle.Instance->DR), length);
+ LL_SPI_EnableDMAReq_TX(_spi.handle.Instance); // Enable Tx DMA Request
+ }
+
+ if (transmitBuf) {
+ HAL_DMA_PollForTransfer(&_dmaTx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
+ HAL_DMA_Abort(&_dmaTx);
+ HAL_DMA_DeInit(&_dmaTx);
+ }
+
+ // while ((_spi.handle.Instance->SR & SPI_FLAG_RXNE) != SPI_FLAG_RXNE) {}
+
+ if (receiveBuf) {
+ HAL_DMA_PollForTransfer(&_dmaRx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
+ HAL_DMA_Abort(&_dmaRx);
+ HAL_DMA_DeInit(&_dmaRx);
+ }
+
+ return 1;
+}
+
+uint8_t MarlinSPI::dmaSend(const void * transmitBuf, uint16_t length, bool minc) {
+ setupDma(_spi.handle, _dmaTx, DMA_MEMORY_TO_PERIPH, minc);
+ HAL_DMA_Start(&_dmaTx, (uint32_t)transmitBuf, (uint32_t)&(_spi.handle.Instance->DR), length);
+ __HAL_SPI_ENABLE(&_spi.handle);
+ LL_SPI_EnableDMAReq_TX(_spi.handle.Instance); // Enable Tx DMA Request
+ HAL_DMA_PollForTransfer(&_dmaTx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
+ HAL_DMA_Abort(&_dmaTx);
+ // DeInit objects
+ HAL_DMA_DeInit(&_dmaTx);
+ return 1;
+}
+
+#endif // HAL_STM32 && !STM32H7xx
diff --git a/Marlin/src/HAL/STM32/MarlinSPI.h b/Marlin/src/HAL/STM32/MarlinSPI.h
new file mode 100644
index 0000000000..fbd3585ff4
--- /dev/null
+++ b/Marlin/src/HAL/STM32/MarlinSPI.h
@@ -0,0 +1,107 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include "HAL.h"
+#include
+
+extern "C" {
+ #include
+}
+
+/**
+ * Marlin currently requires 3 SPI classes:
+ *
+ * SPIClass:
+ * This class is normally provided by frameworks and has a semi-default interface.
+ * This is needed because some libraries reference it globally.
+ *
+ * SPISettings:
+ * Container for SPI configs for SPIClass. As above, libraries may reference it globally.
+ *
+ * These two classes are often provided by frameworks so we cannot extend them to add
+ * useful methods for Marlin.
+ *
+ * MarlinSPI:
+ * Provides the default SPIClass interface plus some Marlin goodies such as a simplified
+ * interface for SPI DMA transfer.
+ *
+ */
+
+#define DATA_SIZE_8BIT SPI_DATASIZE_8BIT
+#define DATA_SIZE_16BIT SPI_DATASIZE_16BIT
+
+class MarlinSPI {
+public:
+ MarlinSPI() : MarlinSPI(NC, NC, NC, NC) {}
+
+ MarlinSPI(pin_t mosi, pin_t miso, pin_t sclk, pin_t ssel = (pin_t)NC) : _mosiPin(mosi), _misoPin(miso), _sckPin(sclk), _ssPin(ssel) {
+ _spi.pin_miso = digitalPinToPinName(_misoPin);
+ _spi.pin_mosi = digitalPinToPinName(_mosiPin);
+ _spi.pin_sclk = digitalPinToPinName(_sckPin);
+ _spi.pin_ssel = digitalPinToPinName(_ssPin);
+ _dataSize = DATA_SIZE_8BIT;
+ _bitOrder = MSBFIRST;
+ _dataMode = SPI_MODE_0;
+ _spi.handle.State = HAL_SPI_STATE_RESET;
+ setClockDivider(SPI_SPEED_CLOCK_DIV2_MHZ);
+ }
+
+ void begin(void);
+ void end(void) {}
+
+ byte transfer(uint8_t _data);
+ uint8_t dmaTransfer(const void *transmitBuf, void *receiveBuf, uint16_t length);
+ uint8_t dmaSend(const void * transmitBuf, uint16_t length, bool minc = true);
+
+ /* These methods are deprecated and kept for compatibility.
+ * Use SPISettings with SPI.beginTransaction() to configure SPI parameters.
+ */
+ void setBitOrder(BitOrder _order) { _bitOrder = _order; }
+
+ void setDataMode(uint8_t _mode) {
+ switch (_mode) {
+ case SPI_MODE0: _dataMode = SPI_MODE_0; break;
+ case SPI_MODE1: _dataMode = SPI_MODE_1; break;
+ case SPI_MODE2: _dataMode = SPI_MODE_2; break;
+ case SPI_MODE3: _dataMode = SPI_MODE_3; break;
+ }
+ }
+
+ void setClockDivider(uint8_t _div);
+
+private:
+ void setupDma(SPI_HandleTypeDef &_spiHandle, DMA_HandleTypeDef &_dmaHandle, uint32_t direction, bool minc = false);
+
+ spi_t _spi;
+ DMA_HandleTypeDef _dmaTx;
+ DMA_HandleTypeDef _dmaRx;
+ BitOrder _bitOrder;
+ spi_mode_e _dataMode;
+ uint8_t _clockDivider;
+ uint32_t _speed;
+ uint32_t _dataSize;
+ pin_t _mosiPin;
+ pin_t _misoPin;
+ pin_t _sckPin;
+ pin_t _ssPin;
+};
diff --git a/Marlin/src/HAL/STM32/MarlinSerial.cpp b/Marlin/src/HAL/STM32/MarlinSerial.cpp
new file mode 100644
index 0000000000..37a8f40fd0
--- /dev/null
+++ b/Marlin/src/HAL/STM32/MarlinSerial.cpp
@@ -0,0 +1,110 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfig.h"
+#include "MarlinSerial.h"
+
+#if ENABLED(EMERGENCY_PARSER)
+ #include "../../feature/e_parser.h"
+#endif
+
+#ifndef USART4
+ #define USART4 UART4
+#endif
+#ifndef USART5
+ #define USART5 UART5
+#endif
+
+#define DECLARE_SERIAL_PORT(ser_num) \
+ void _rx_complete_irq_ ## ser_num (serial_t * obj); \
+ MSerialT MSerial ## ser_num (true, USART ## ser_num, &_rx_complete_irq_ ## ser_num); \
+ void _rx_complete_irq_ ## ser_num (serial_t * obj) { MSerial ## ser_num ._rx_complete_irq(obj); }
+
+#if USING_HW_SERIAL1
+ DECLARE_SERIAL_PORT(1)
+#endif
+#if USING_HW_SERIAL2
+ DECLARE_SERIAL_PORT(2)
+#endif
+#if USING_HW_SERIAL3
+ DECLARE_SERIAL_PORT(3)
+#endif
+#if USING_HW_SERIAL4
+ DECLARE_SERIAL_PORT(4)
+#endif
+#if USING_HW_SERIAL5
+ DECLARE_SERIAL_PORT(5)
+#endif
+#if USING_HW_SERIAL6
+ DECLARE_SERIAL_PORT(6)
+#endif
+#if USING_HW_SERIAL7
+ DECLARE_SERIAL_PORT(7)
+#endif
+#if USING_HW_SERIAL8
+ DECLARE_SERIAL_PORT(8)
+#endif
+#if USING_HW_SERIAL9
+ DECLARE_SERIAL_PORT(9)
+#endif
+#if USING_HW_SERIAL10
+ DECLARE_SERIAL_PORT(10)
+#endif
+#if USING_HW_SERIALLP1
+ DECLARE_SERIAL_PORT(LP1)
+#endif
+
+void MarlinSerial::begin(unsigned long baud, uint8_t config) {
+ HardwareSerial::begin(baud, config);
+ // Replace the IRQ callback with the one we have defined
+ TERN_(EMERGENCY_PARSER, _serial.rx_callback = _rx_callback);
+}
+
+// This function is Copyright (c) 2006 Nicholas Zambetti.
+void MarlinSerial::_rx_complete_irq(serial_t *obj) {
+ // No Parity error, read byte and store it in the buffer if there is room
+ unsigned char c;
+
+ if (uart_getc(obj, &c) == 0) {
+
+ rx_buffer_index_t i = (unsigned int)(obj->rx_head + 1) % SERIAL_RX_BUFFER_SIZE;
+
+ // if we should be storing the received character into the location
+ // just before the tail (meaning that the head would advance to the
+ // current location of the tail), we're about to overflow the buffer
+ // and so we don't write the character or advance the head.
+ if (i != obj->rx_tail) {
+ obj->rx_buff[obj->rx_head] = c;
+ obj->rx_head = i;
+ }
+
+ #if ENABLED(EMERGENCY_PARSER)
+ emergency_parser.update(static_cast(this)->emergency_state, c);
+ #endif
+ }
+}
+
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/MarlinSerial.h b/Marlin/src/HAL/STM32/MarlinSerial.h
new file mode 100644
index 0000000000..bf861fb8a7
--- /dev/null
+++ b/Marlin/src/HAL/STM32/MarlinSerial.h
@@ -0,0 +1,59 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if ENABLED(EMERGENCY_PARSER)
+ #include "../../feature/e_parser.h"
+#endif
+
+#include "../../core/serial_hook.h"
+
+typedef void (*usart_rx_callback_t)(serial_t * obj);
+
+struct MarlinSerial : public HardwareSerial {
+ MarlinSerial(void *peripheral, usart_rx_callback_t rx_callback) :
+ HardwareSerial(peripheral), _rx_callback(rx_callback)
+ { }
+
+ void begin(unsigned long baud, uint8_t config);
+ inline void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
+
+ void _rx_complete_irq(serial_t *obj);
+
+protected:
+ usart_rx_callback_t _rx_callback;
+};
+
+typedef Serial1Class MSerialT;
+extern MSerialT MSerial1;
+extern MSerialT MSerial2;
+extern MSerialT MSerial3;
+extern MSerialT MSerial4;
+extern MSerialT MSerial5;
+extern MSerialT MSerial6;
+extern MSerialT MSerial7;
+extern MSerialT MSerial8;
+extern MSerialT MSerial9;
+extern MSerialT MSerial10;
+extern MSerialT MSerialLP1;
diff --git a/Marlin/src/HAL/STM32/MinSerial.cpp b/Marlin/src/HAL/STM32/MinSerial.cpp
new file mode 100644
index 0000000000..b0fcff20c1
--- /dev/null
+++ b/Marlin/src/HAL/STM32/MinSerial.cpp
@@ -0,0 +1,153 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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 "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if ENABLED(POSTMORTEM_DEBUGGING)
+
+#include "../shared/MinSerial.h"
+
+/* Instruction Synchronization Barrier */
+#define isb() __asm__ __volatile__ ("isb" : : : "memory")
+
+/* Data Synchronization Barrier */
+#define dsb() __asm__ __volatile__ ("dsb" : : : "memory")
+
+// Dumb mapping over the registers of a USART device on STM32
+struct USARTMin {
+ volatile uint32_t SR;
+ volatile uint32_t DR;
+ volatile uint32_t BRR;
+ volatile uint32_t CR1;
+ volatile uint32_t CR2;
+};
+
+#if WITHIN(SERIAL_PORT, 1, 6)
+ // Depending on the CPU, the serial port is different for USART1
+ static const uintptr_t regsAddr[] = {
+ TERN(STM32F1xx, 0x40013800, 0x40011000), // USART1
+ 0x40004400, // USART2
+ 0x40004800, // USART3
+ 0x40004C00, // UART4_BASE
+ 0x40005000, // UART5_BASE
+ 0x40011400 // USART6
+ };
+ static USARTMin * regs = (USARTMin*)regsAddr[SERIAL_PORT - 1];
+#endif
+
+static void TXBegin() {
+ #if !WITHIN(SERIAL_PORT, 1, 6)
+ #warning "Using POSTMORTEM_DEBUGGING requires a physical U(S)ART hardware in case of severe error."
+ #warning "Disabling the severe error reporting feature currently because the used serial port is not a HW port."
+ #else
+ // This is common between STM32F1/STM32F2 and STM32F4
+ const int nvicUART[] = { /* NVIC_USART1 */ 37, /* NVIC_USART2 */ 38, /* NVIC_USART3 */ 39, /* NVIC_UART4 */ 52, /* NVIC_UART5 */ 53, /* NVIC_USART6 */ 71 };
+ int nvicIndex = nvicUART[SERIAL_PORT - 1];
+
+ struct NVICMin {
+ volatile uint32_t ISER[32];
+ volatile uint32_t ICER[32];
+ };
+
+ NVICMin *nvicBase = (NVICMin*)0xE000E100;
+ SBI32(nvicBase->ICER[nvicIndex >> 5], nvicIndex & 0x1F);
+
+ // We NEED memory barriers to ensure Interrupts are actually disabled!
+ // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
+ dsb();
+ isb();
+
+ // Example for USART1 disable: (RCC->APB2ENR &= ~(RCC_APB2ENR_USART1EN))
+ // Too difficult to reimplement here, let's query the STM32duino macro here
+ #if SERIAL_PORT == 1
+ __HAL_RCC_USART1_CLK_DISABLE();
+ __HAL_RCC_USART1_CLK_ENABLE();
+ #elif SERIAL_PORT == 2
+ __HAL_RCC_USART2_CLK_DISABLE();
+ __HAL_RCC_USART2_CLK_ENABLE();
+ #elif SERIAL_PORT == 3
+ __HAL_RCC_USART3_CLK_DISABLE();
+ __HAL_RCC_USART3_CLK_ENABLE();
+ #elif SERIAL_PORT == 4
+ __HAL_RCC_UART4_CLK_DISABLE(); // BEWARE: UART4 and not USART4 here
+ __HAL_RCC_UART4_CLK_ENABLE();
+ #elif SERIAL_PORT == 5
+ __HAL_RCC_UART5_CLK_DISABLE(); // BEWARE: UART5 and not USART5 here
+ __HAL_RCC_UART5_CLK_ENABLE();
+ #elif SERIAL_PORT == 6
+ __HAL_RCC_USART6_CLK_DISABLE();
+ __HAL_RCC_USART6_CLK_ENABLE();
+ #endif
+
+ uint32_t brr = regs->BRR;
+ regs->CR1 = 0; // Reset the USART
+ regs->CR2 = 0; // 1 stop bit
+
+ // If we don't touch the BRR (baudrate register), we don't need to recompute.
+ regs->BRR = brr;
+
+ regs->CR1 = _BV(3) | _BV(13); // 8 bits, no parity, 1 stop bit (TE | UE)
+ #endif
+}
+
+// A SW memory barrier, to ensure GCC does not overoptimize loops
+#define sw_barrier() __asm__ volatile("": : :"memory");
+static void TX(char c) {
+ #if WITHIN(SERIAL_PORT, 1, 6)
+ constexpr uint32_t usart_sr_txe = _BV(7);
+ while (!(regs->SR & usart_sr_txe)) {
+ hal.watchdog_refresh();
+ sw_barrier();
+ }
+ regs->DR = c;
+ #else
+ // Let's hope a mystical guru will fix this, one day by writing interrupt-free USB CDC ACM code (or, at least, by polling the registers since interrupt will be queued but will never trigger)
+ // For now, it's completely lost to oblivion.
+ #endif
+}
+
+void install_min_serial() {
+ HAL_min_serial_init = &TXBegin;
+ HAL_min_serial_out = &TX;
+}
+
+#if NONE(DYNAMIC_VECTORTABLE, STM32F0xx, STM32G0xx) // Cortex M0 can't jump to a symbol that's too far from the current function, so we work around this in exception_arm.cpp
+extern "C" {
+ __attribute__((naked)) void JumpHandler_ASM() {
+ __asm__ __volatile__ (
+ "b CommonHandler_ASM\n"
+ );
+ }
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) HardFault_Handler();
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) BusFault_Handler();
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) UsageFault_Handler();
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) MemManage_Handler();
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) NMI_Handler();
+}
+#endif
+
+#endif // POSTMORTEM_DEBUGGING
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/README.md b/Marlin/src/HAL/STM32/README.md
new file mode 100644
index 0000000000..7680df6654
--- /dev/null
+++ b/Marlin/src/HAL/STM32/README.md
@@ -0,0 +1,11 @@
+# Generic STM32 HAL based on the stm32duino core
+
+This HAL is intended to act as the generic STM32 HAL for all STM32 chips (The whole F, H and L family).
+
+Currently it supports:
+ * STM32F0xx
+ * STM32F1xx
+ * STM32F4xx
+ * STM32F7xx
+
+Targeting the official [Arduino STM32 Core](https://github.com/stm32duino/Arduino_Core_STM32).
diff --git a/Marlin/src/HAL/STM32/Servo.cpp b/Marlin/src/HAL/STM32/Servo.cpp
new file mode 100644
index 0000000000..a00186e0e7
--- /dev/null
+++ b/Marlin/src/HAL/STM32/Servo.cpp
@@ -0,0 +1,112 @@
+/**
+ * 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
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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 "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfig.h"
+
+#if HAS_SERVOS
+
+#include "Servo.h"
+
+static uint_fast8_t servoCount = 0;
+static libServo *servos[NUM_SERVOS] = {0};
+constexpr millis_t servoDelay[] = SERVO_DELAY;
+static_assert(COUNT(servoDelay) == NUM_SERVOS, "SERVO_DELAY must be an array NUM_SERVOS long.");
+
+// Initialize to the default timer priority. This will be overridden by a call from timers.cpp.
+// This allows all timer interrupt priorities to be managed from a single location in the HAL.
+static uint32_t servo_interrupt_priority = NVIC_EncodePriority(NVIC_GetPriorityGrouping(), TIM_IRQ_PRIO, TIM_IRQ_SUBPRIO);
+
+// This must be called after the STM32 Servo class has initialized the timer.
+// It may only be needed after the first call to attach(), but it is possible
+// that is is necessary after every detach() call. To be safe this is currently
+// called after every call to attach().
+static void fixServoTimerInterruptPriority() {
+ NVIC_SetPriority(getTimerUpIrq(TIMER_SERVO), servo_interrupt_priority);
+}
+
+libServo::libServo()
+: delay(servoDelay[servoCount]),
+ was_attached_before_pause(false),
+ value_before_pause(0)
+{
+ servos[servoCount++] = this;
+}
+
+int8_t libServo::attach(const int pin) {
+ if (servoCount >= MAX_SERVOS) return -1;
+ if (pin > 0) servo_pin = pin;
+ auto result = stm32_servo.attach(servo_pin);
+ fixServoTimerInterruptPriority();
+ return result;
+}
+
+int8_t libServo::attach(const int pin, const int min, const int max) {
+ if (servoCount >= MAX_SERVOS) return -1;
+ if (pin > 0) servo_pin = pin;
+ auto result = stm32_servo.attach(servo_pin, min, max);
+ fixServoTimerInterruptPriority();
+ return result;
+}
+
+void libServo::move(const int value) {
+ if (attach(0) >= 0) {
+ stm32_servo.write(value);
+ safe_delay(delay);
+ TERN_(DEACTIVATE_SERVOS_AFTER_MOVE, detach());
+ }
+}
+
+void libServo::pause() {
+ was_attached_before_pause = stm32_servo.attached();
+ if (was_attached_before_pause) {
+ value_before_pause = stm32_servo.read();
+ stm32_servo.detach();
+ }
+}
+
+void libServo::resume() {
+ if (was_attached_before_pause) {
+ attach();
+ move(value_before_pause);
+ }
+}
+
+void libServo::pause_all_servos() {
+ for (auto& servo : servos)
+ if (servo) servo->pause();
+}
+
+void libServo::resume_all_servos() {
+ for (auto& servo : servos)
+ if (servo) servo->resume();
+}
+
+void libServo::setInterruptPriority(uint32_t preemptPriority, uint32_t subPriority) {
+ servo_interrupt_priority = NVIC_EncodePriority(NVIC_GetPriorityGrouping(), preemptPriority, subPriority);
+}
+
+#endif // HAS_SERVOS
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/Servo.h b/Marlin/src/HAL/STM32/Servo.h
new file mode 100644
index 0000000000..1527e753b6
--- /dev/null
+++ b/Marlin/src/HAL/STM32/Servo.h
@@ -0,0 +1,54 @@
+/**
+ * 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
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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
+
+#include "../../core/millis_t.h"
+
+// Inherit and expand on the official library
+class libServo {
+ public:
+ libServo();
+ int8_t attach(const int pin = 0); // pin == 0 uses value from previous call
+ int8_t attach(const int pin, const int min, const int max);
+ void detach() { stm32_servo.detach(); }
+ int read() { return stm32_servo.read(); }
+ void move(const int value);
+
+ void pause();
+ void resume();
+
+ static void pause_all_servos();
+ static void resume_all_servos();
+ static void setInterruptPriority(uint32_t preemptPriority, uint32_t subPriority);
+
+ private:
+ Servo stm32_servo;
+
+ int servo_pin = 0;
+ millis_t delay = 0;
+
+ bool was_attached_before_pause;
+ int value_before_pause;
+};
diff --git a/Marlin/src/HAL/STM32/eeprom_bl24cxx.cpp b/Marlin/src/HAL/STM32/eeprom_bl24cxx.cpp
new file mode 100644
index 0000000000..f30b3dedb2
--- /dev/null
+++ b/Marlin/src/HAL/STM32/eeprom_bl24cxx.cpp
@@ -0,0 +1,85 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../platforms.h"
+
+#ifdef HAL_STM32
+
+/**
+ * PersistentStore for Arduino-style EEPROM interface
+ * with simple implementations supplied by Marlin.
+ */
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(IIC_BL24CXX_EEPROM)
+
+#include "../shared/eeprom_if.h"
+#include "../shared/eeprom_api.h"
+
+//
+// PersistentStore
+//
+
+#ifndef MARLIN_EEPROM_SIZE
+ #error "MARLIN_EEPROM_SIZE is required for IIC_BL24CXX_EEPROM."
+#endif
+
+size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; }
+
+bool PersistentStore::access_start() { eeprom_init(); return true; }
+bool PersistentStore::access_finish() { return true; }
+
+bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+ uint16_t written = 0;
+ while (size--) {
+ uint8_t v = *value;
+ uint8_t * const p = (uint8_t * const)pos;
+ if (v != eeprom_read_byte(p)) { // EEPROM has only ~100,000 write cycles, so only write bytes that have changed!
+ eeprom_write_byte(p, v);
+ if (++written & 0x7F) delay(2); else safe_delay(2); // Avoid triggering watchdog during long EEPROM writes
+ if (eeprom_read_byte(p) != v) {
+ SERIAL_ECHO_MSG(STR_ERR_EEPROM_WRITE);
+ return true;
+ }
+ }
+ crc16(crc, &v, 1);
+ pos++;
+ value++;
+ }
+ return false;
+}
+
+bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
+ do {
+ uint8_t * const p = (uint8_t * const)pos;
+ uint8_t c = eeprom_read_byte(p);
+ if (writing) *value = c;
+ crc16(crc, &c, 1);
+ pos++;
+ value++;
+ } while (--size);
+ return false;
+}
+
+#endif // IIC_BL24CXX_EEPROM
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/eeprom_flash.cpp b/Marlin/src/HAL/STM32/eeprom_flash.cpp
new file mode 100644
index 0000000000..6bd519877d
--- /dev/null
+++ b/Marlin/src/HAL/STM32/eeprom_flash.cpp
@@ -0,0 +1,279 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
+ * Copyright (c) 2016 Victor Perez victor_pv@hotmail.com
+ *
+ * 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 "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(FLASH_EEPROM_EMULATION)
+
+#include "../shared/eeprom_api.h"
+
+// Better: "utility/stm32_eeprom.h", but only after updating stm32duino to 2.0.0
+// Use EEPROM.h for compatibility, for now.
+#include
+
+/**
+ * The STM32 HAL supports chips that deal with "pages" and some with "sectors" and some that
+ * even have multiple "banks" of flash.
+ *
+ * This code is a bit of a mashup of
+ * framework-arduinoststm32/cores/arduino/stm32/stm32_eeprom.c
+ * hal/hal_lpc1768/persistent_store_flash.cpp
+ *
+ * This has only be written against those that use a single "sector" design.
+ *
+ * Those that deal with "pages" could be made to work. Looking at the STM32F07 for example, there are
+ * 128 "pages", each 2kB in size. If we continued with our EEPROM being 4Kb, we'd always need to operate
+ * on 2 of these pages. Each write, we'd use 2 different pages from a pool of pages until we are done.
+ */
+
+#if ENABLED(FLASH_EEPROM_LEVELING)
+
+ #include "stm32_def.h"
+
+ #define DEBUG_OUT ENABLED(EEPROM_CHITCHAT)
+ #include "../../core/debug_out.h"
+
+ #ifndef MARLIN_EEPROM_SIZE
+ #define MARLIN_EEPROM_SIZE 0x1000 // 4KB
+ #endif
+
+ #ifndef FLASH_SECTOR
+ #define FLASH_SECTOR (FLASH_SECTOR_TOTAL - 1)
+ #endif
+ #ifndef FLASH_UNIT_SIZE
+ #define FLASH_UNIT_SIZE 0x20000 // 128kB
+ #endif
+
+ #ifndef FLASH_ADDRESS_START
+ #define FLASH_ADDRESS_START (FLASH_END - ((FLASH_SECTOR_TOTAL - (FLASH_SECTOR)) * (FLASH_UNIT_SIZE)) + 1)
+ #endif
+ #define FLASH_ADDRESS_END (FLASH_ADDRESS_START + FLASH_UNIT_SIZE - 1)
+
+ #define EEPROM_SLOTS ((FLASH_UNIT_SIZE) / (MARLIN_EEPROM_SIZE))
+ #define SLOT_ADDRESS(slot) (FLASH_ADDRESS_START + (slot * (MARLIN_EEPROM_SIZE)))
+
+ #define UNLOCK_FLASH() if (!flash_unlocked) { \
+ HAL_FLASH_Unlock(); \
+ __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | \
+ FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); \
+ flash_unlocked = true; \
+ }
+ #define LOCK_FLASH() if (flash_unlocked) { HAL_FLASH_Lock(); flash_unlocked = false; }
+
+ #define EMPTY_UINT32 ((uint32_t)-1)
+ #define EMPTY_UINT8 ((uint8_t)-1)
+
+ static uint8_t ram_eeprom[MARLIN_EEPROM_SIZE] __attribute__((aligned(4))) = {0};
+ static int current_slot = -1;
+
+ static_assert(0 == MARLIN_EEPROM_SIZE % 4, "MARLIN_EEPROM_SIZE must be a multiple of 4"); // Ensure copying as uint32_t is safe
+ static_assert(0 == FLASH_UNIT_SIZE % MARLIN_EEPROM_SIZE, "MARLIN_EEPROM_SIZE must divide evenly into your FLASH_UNIT_SIZE");
+ static_assert(FLASH_UNIT_SIZE >= MARLIN_EEPROM_SIZE, "FLASH_UNIT_SIZE must be greater than or equal to your MARLIN_EEPROM_SIZE");
+ static_assert(IS_FLASH_SECTOR(FLASH_SECTOR), "FLASH_SECTOR is invalid");
+ static_assert(IS_POWER_OF_2(FLASH_UNIT_SIZE), "FLASH_UNIT_SIZE should be a power of 2, please check your chip's spec sheet");
+
+#endif // FLASH_EEPROM_LEVELING
+
+static bool eeprom_data_written = false;
+
+#ifndef MARLIN_EEPROM_SIZE
+ #define MARLIN_EEPROM_SIZE size_t(E2END + 1)
+#endif
+size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; }
+
+bool PersistentStore::access_start() {
+
+ EEPROM.begin(); // Avoid STM32 EEPROM.h warning (do nothing)
+
+ #if ENABLED(FLASH_EEPROM_LEVELING)
+
+ if (current_slot == -1 || eeprom_data_written) {
+ // This must be the first time since power on that we have accessed the storage, or someone
+ // loaded and called write_data and never called access_finish.
+ // Lets go looking for the slot that holds our configuration.
+ if (eeprom_data_written) DEBUG_ECHOLNPGM("Dangling EEPROM write_data");
+ uint32_t address = FLASH_ADDRESS_START;
+ while (address <= FLASH_ADDRESS_END) {
+ uint32_t address_value = (*(__IO uint32_t*)address);
+ if (address_value != EMPTY_UINT32) {
+ current_slot = (address - (FLASH_ADDRESS_START)) / (MARLIN_EEPROM_SIZE);
+ break;
+ }
+ address += sizeof(uint32_t);
+ }
+ if (current_slot == -1) {
+ // We didn't find anything, so we'll just initialize to empty
+ for (int i = 0; i < MARLIN_EEPROM_SIZE; i++) ram_eeprom[i] = EMPTY_UINT8;
+ current_slot = EEPROM_SLOTS;
+ }
+ else {
+ // load current settings
+ uint8_t *eeprom_data = (uint8_t *)SLOT_ADDRESS(current_slot);
+ for (int i = 0; i < MARLIN_EEPROM_SIZE; i++) ram_eeprom[i] = eeprom_data[i];
+ DEBUG_ECHOLNPGM("EEPROM loaded from slot ", current_slot, ".");
+ }
+ eeprom_data_written = false;
+ }
+
+ #else
+ eeprom_buffer_fill();
+ #endif
+
+ return true;
+}
+
+bool PersistentStore::access_finish() {
+
+ if (eeprom_data_written) {
+ #ifdef STM32F4xx
+ // MCU may come up with flash error bits which prevent some flash operations.
+ // Clear flags prior to flash operations to prevent errors.
+ __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
+ #endif
+
+ #if ENABLED(FLASH_EEPROM_LEVELING)
+
+ HAL_StatusTypeDef status = HAL_ERROR;
+ bool flash_unlocked = false;
+
+ if (--current_slot < 0) {
+ // all slots have been used, erase everything and start again
+
+ FLASH_EraseInitTypeDef EraseInitStruct;
+ uint32_t SectorError = 0;
+
+ EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
+ EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
+ EraseInitStruct.Sector = FLASH_SECTOR;
+ EraseInitStruct.NbSectors = 1;
+
+ current_slot = EEPROM_SLOTS - 1;
+ UNLOCK_FLASH();
+
+ TERN_(HAS_PAUSE_SERVO_OUTPUT, PAUSE_SERVO_OUTPUT());
+ hal.isr_off();
+ status = HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError);
+ hal.isr_on();
+ TERN_(HAS_PAUSE_SERVO_OUTPUT, RESUME_SERVO_OUTPUT());
+ if (status != HAL_OK) {
+ DEBUG_ECHOLNPGM("HAL_FLASHEx_Erase=", status);
+ DEBUG_ECHOLNPGM("GetError=", HAL_FLASH_GetError());
+ DEBUG_ECHOLNPGM("SectorError=", SectorError);
+ LOCK_FLASH();
+ return false;
+ }
+ }
+
+ UNLOCK_FLASH();
+
+ uint32_t offset = 0,
+ address = SLOT_ADDRESS(current_slot),
+ address_end = address + MARLIN_EEPROM_SIZE,
+ data = 0;
+
+ bool success = true;
+
+ while (address < address_end) {
+ memcpy(&data, ram_eeprom + offset, sizeof(data));
+ status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, data);
+ if (status == HAL_OK) {
+ address += sizeof(uint32_t);
+ offset += sizeof(uint32_t);
+ }
+ else {
+ DEBUG_ECHOLNPGM("HAL_FLASH_Program=", status);
+ DEBUG_ECHOLNPGM("GetError=", HAL_FLASH_GetError());
+ DEBUG_ECHOLNPGM("address=", address);
+ success = false;
+ break;
+ }
+ }
+
+ LOCK_FLASH();
+
+ if (success) {
+ eeprom_data_written = false;
+ DEBUG_ECHOLNPGM("EEPROM saved to slot ", current_slot, ".");
+ }
+
+ return success;
+
+ #else // !FLASH_EEPROM_LEVELING
+
+ // The following was written for the STM32F4 but may work with other MCUs as well.
+ // Most STM32F4 flash does not allow reading from flash during erase operations.
+ // This takes about a second on a STM32F407 with a 128kB sector used as EEPROM.
+ // Interrupts during this time can have unpredictable results, such as killing Servo
+ // output. Servo output still glitches with interrupts disabled, but recovers after the
+ // erase.
+ TERN_(HAS_PAUSE_SERVO_OUTPUT, PAUSE_SERVO_OUTPUT());
+ hal.isr_off();
+ eeprom_buffer_flush();
+ hal.isr_on();
+ TERN_(HAS_PAUSE_SERVO_OUTPUT, RESUME_SERVO_OUTPUT());
+
+ eeprom_data_written = false;
+
+ #endif // !FLASH_EEPROM_LEVELING
+ }
+
+ return true;
+}
+
+bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+ while (size--) {
+ uint8_t v = *value;
+ #if ENABLED(FLASH_EEPROM_LEVELING)
+ if (v != ram_eeprom[pos]) {
+ ram_eeprom[pos] = v;
+ eeprom_data_written = true;
+ }
+ #else
+ if (v != eeprom_buffered_read_byte(pos)) {
+ eeprom_buffered_write_byte(pos, v);
+ eeprom_data_written = true;
+ }
+ #endif
+ crc16(crc, &v, 1);
+ pos++;
+ value++;
+ }
+ return false;
+}
+
+bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
+ do {
+ const uint8_t c = TERN(FLASH_EEPROM_LEVELING, ram_eeprom[pos], eeprom_buffered_read_byte(pos));
+ if (writing) *value = c;
+ crc16(crc, &c, 1);
+ pos++;
+ value++;
+ } while (--size);
+ return false;
+}
+
+#endif // FLASH_EEPROM_EMULATION
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/eeprom_if_iic.cpp b/Marlin/src/HAL/STM32/eeprom_if_iic.cpp
new file mode 100644
index 0000000000..ad8712c0c0
--- /dev/null
+++ b/Marlin/src/HAL/STM32/eeprom_if_iic.cpp
@@ -0,0 +1,56 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../platforms.h"
+
+#ifdef HAL_STM32
+
+/**
+ * Platform-independent Arduino functions for I2C EEPROM.
+ * Enable USE_SHARED_EEPROM if not supplied by the framework.
+ */
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(IIC_BL24CXX_EEPROM)
+
+#include "../../libs/BL24CXX.h"
+#include "../shared/eeprom_if.h"
+
+void eeprom_init() { BL24CXX::init(); }
+
+// ------------------------
+// Public functions
+// ------------------------
+
+void eeprom_write_byte(uint8_t *pos, uint8_t value) {
+ const unsigned eeprom_address = (unsigned)pos;
+ return BL24CXX::writeOneByte(eeprom_address, value);
+}
+
+uint8_t eeprom_read_byte(uint8_t *pos) {
+ const unsigned eeprom_address = (unsigned)pos;
+ return BL24CXX::readOneByte(eeprom_address);
+}
+
+#endif // IIC_BL24CXX_EEPROM
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/eeprom_sdcard.cpp b/Marlin/src/HAL/STM32/eeprom_sdcard.cpp
new file mode 100644
index 0000000000..1b5c0ae5b2
--- /dev/null
+++ b/Marlin/src/HAL/STM32/eeprom_sdcard.cpp
@@ -0,0 +1,94 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../platforms.h"
+
+#ifdef HAL_STM32
+
+/**
+ * Implementation of EEPROM settings in SD Card
+ */
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(SDCARD_EEPROM_EMULATION)
+
+#include "../shared/eeprom_api.h"
+#include "../../sd/cardreader.h"
+
+#define EEPROM_FILENAME "eeprom.dat"
+
+#ifndef MARLIN_EEPROM_SIZE
+ #define MARLIN_EEPROM_SIZE 0x1000 // 4KB
+#endif
+size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; }
+
+#define _ALIGN(x) __attribute__ ((aligned(x)))
+static char _ALIGN(4) HAL_eeprom_data[MARLIN_EEPROM_SIZE];
+
+bool PersistentStore::access_start() {
+ if (!card.isMounted()) return false;
+
+ MediaFile file, root = card.getroot();
+ if (!file.open(&root, EEPROM_FILENAME, O_RDONLY))
+ return true;
+
+ int bytes_read = file.read(HAL_eeprom_data, MARLIN_EEPROM_SIZE);
+ if (bytes_read < 0) return false;
+ for (; bytes_read < MARLIN_EEPROM_SIZE; bytes_read++)
+ HAL_eeprom_data[bytes_read] = 0xFF;
+ file.close();
+ return true;
+}
+
+bool PersistentStore::access_finish() {
+ if (!card.isMounted()) return false;
+
+ MediaFile file, root = card.getroot();
+ int bytes_written = 0;
+ if (file.open(&root, EEPROM_FILENAME, O_CREAT | O_WRITE | O_TRUNC)) {
+ bytes_written = file.write(HAL_eeprom_data, MARLIN_EEPROM_SIZE);
+ file.close();
+ }
+ return (bytes_written == MARLIN_EEPROM_SIZE);
+}
+
+bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+ for (size_t i = 0; i < size; i++)
+ HAL_eeprom_data[pos + i] = value[i];
+ crc16(crc, value, size);
+ pos += size;
+ return false;
+}
+
+bool PersistentStore::read_data(int &pos, uint8_t *value, const size_t size, uint16_t *crc, const bool writing/*=true*/) {
+ for (size_t i = 0; i < size; i++) {
+ uint8_t c = HAL_eeprom_data[pos + i];
+ if (writing) value[i] = c;
+ crc16(crc, &c, 1);
+ }
+ pos += size;
+ return false;
+}
+
+#endif // SDCARD_EEPROM_EMULATION
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/eeprom_sram.cpp b/Marlin/src/HAL/STM32/eeprom_sram.cpp
new file mode 100644
index 0000000000..687e7f55c2
--- /dev/null
+++ b/Marlin/src/HAL/STM32/eeprom_sram.cpp
@@ -0,0 +1,70 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
+ * Copyright (c) 2016 Victor Perez victor_pv@hotmail.com
+ *
+ * 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 "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(SRAM_EEPROM_EMULATION)
+
+#include "../shared/eeprom_if.h"
+#include "../shared/eeprom_api.h"
+
+#ifndef MARLIN_EEPROM_SIZE
+ #define MARLIN_EEPROM_SIZE 0x1000 // 4KB
+#endif
+size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; }
+
+bool PersistentStore::access_start() { return true; }
+bool PersistentStore::access_finish() { return true; }
+
+bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+ while (size--) {
+ uint8_t v = *value;
+
+ // Save to Backup SRAM
+ *(__IO uint8_t *)(BKPSRAM_BASE + (uint8_t * const)pos) = v;
+
+ crc16(crc, &v, 1);
+ pos++;
+ value++;
+ };
+
+ return false;
+}
+
+bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
+ do {
+ // Read from either external EEPROM, program flash or Backup SRAM
+ const uint8_t c = ( *(__IO uint8_t *)(BKPSRAM_BASE + ((uint8_t*)pos)) );
+ if (writing) *value = c;
+ crc16(crc, &c, 1);
+ pos++;
+ value++;
+ } while (--size);
+ return false;
+}
+
+#endif // SRAM_EEPROM_EMULATION
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/eeprom_wired.cpp b/Marlin/src/HAL/STM32/eeprom_wired.cpp
new file mode 100644
index 0000000000..cf0468151e
--- /dev/null
+++ b/Marlin/src/HAL/STM32/eeprom_wired.cpp
@@ -0,0 +1,80 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
+ * Copyright (c) 2016 Victor Perez victor_pv@hotmail.com
+ *
+ * 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 "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfig.h"
+
+#if USE_WIRED_EEPROM
+
+/**
+ * PersistentStore for Arduino-style EEPROM interface
+ * with simple implementations supplied by Marlin.
+ */
+
+#include "../shared/eeprom_if.h"
+#include "../shared/eeprom_api.h"
+
+#ifndef MARLIN_EEPROM_SIZE
+ #define MARLIN_EEPROM_SIZE size_t(E2END + 1)
+#endif
+size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; }
+
+bool PersistentStore::access_start() { eeprom_init(); return true; }
+bool PersistentStore::access_finish() { return true; }
+
+bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+ uint16_t written = 0;
+ while (size--) {
+ uint8_t v = *value;
+ uint8_t * const p = (uint8_t * const)pos;
+ if (v != eeprom_read_byte(p)) { // EEPROM has only ~100,000 write cycles, so only write bytes that have changed!
+ eeprom_write_byte(p, v);
+ if (++written & 0x7F) delay(2); else safe_delay(2); // Avoid triggering watchdog during long EEPROM writes
+ if (eeprom_read_byte(p) != v) {
+ SERIAL_ECHO_MSG(STR_ERR_EEPROM_WRITE);
+ return true;
+ }
+ }
+ crc16(crc, &v, 1);
+ pos++;
+ value++;
+ }
+ return false;
+}
+
+bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
+ do {
+ // Read from either external EEPROM, program flash or Backup SRAM
+ const uint8_t c = eeprom_read_byte((uint8_t*)pos);
+ if (writing) *value = c;
+ crc16(crc, &c, 1);
+ pos++;
+ value++;
+ } while (--size);
+ return false;
+}
+
+#endif // USE_WIRED_EEPROM
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/endstop_interrupts.h b/Marlin/src/HAL/STM32/endstop_interrupts.h
new file mode 100644
index 0000000000..d2f20ba1c7
--- /dev/null
+++ b/Marlin/src/HAL/STM32/endstop_interrupts.h
@@ -0,0 +1,61 @@
+/**
+ * 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
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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 "../../module/endstops.h"
+
+// One ISR for all EXT-Interrupts
+void endstop_ISR() { endstops.update(); }
+
+void setup_endstop_interrupts() {
+ #define _ATTACH(P) attachInterrupt(P, endstop_ISR, CHANGE)
+ TERN_(HAS_X_MAX, _ATTACH(X_MAX_PIN));
+ TERN_(HAS_X_MIN, _ATTACH(X_MIN_PIN));
+ TERN_(HAS_Y_MAX, _ATTACH(Y_MAX_PIN));
+ TERN_(HAS_Y_MIN, _ATTACH(Y_MIN_PIN));
+ TERN_(HAS_Z_MAX, _ATTACH(Z_MAX_PIN));
+ TERN_(HAS_Z_MIN, _ATTACH(Z_MIN_PIN));
+ TERN_(HAS_X2_MAX, _ATTACH(X2_MAX_PIN));
+ TERN_(HAS_X2_MIN, _ATTACH(X2_MIN_PIN));
+ TERN_(HAS_Y2_MAX, _ATTACH(Y2_MAX_PIN));
+ TERN_(HAS_Y2_MIN, _ATTACH(Y2_MIN_PIN));
+ TERN_(HAS_Z2_MAX, _ATTACH(Z2_MAX_PIN));
+ TERN_(HAS_Z2_MIN, _ATTACH(Z2_MIN_PIN));
+ TERN_(HAS_Z3_MAX, _ATTACH(Z3_MAX_PIN));
+ TERN_(HAS_Z3_MIN, _ATTACH(Z3_MIN_PIN));
+ TERN_(HAS_Z4_MAX, _ATTACH(Z4_MAX_PIN));
+ TERN_(HAS_Z4_MIN, _ATTACH(Z4_MIN_PIN));
+ TERN_(HAS_Z_MIN_PROBE_PIN, _ATTACH(Z_MIN_PROBE_PIN));
+ TERN_(HAS_I_MAX, _ATTACH(I_MAX_PIN));
+ TERN_(HAS_I_MIN, _ATTACH(I_MIN_PIN));
+ TERN_(HAS_J_MAX, _ATTACH(J_MAX_PIN));
+ TERN_(HAS_J_MIN, _ATTACH(J_MIN_PIN));
+ TERN_(HAS_K_MAX, _ATTACH(K_MAX_PIN));
+ TERN_(HAS_K_MIN, _ATTACH(K_MIN_PIN));
+ TERN_(HAS_U_MAX, _ATTACH(U_MAX_PIN));
+ TERN_(HAS_U_MIN, _ATTACH(U_MIN_PIN));
+ TERN_(HAS_V_MAX, _ATTACH(V_MAX_PIN));
+ TERN_(HAS_V_MIN, _ATTACH(V_MIN_PIN));
+ TERN_(HAS_W_MAX, _ATTACH(W_MAX_PIN));
+ TERN_(HAS_W_MIN, _ATTACH(W_MIN_PIN));
+}
diff --git a/Marlin/src/HAL/STM32/fast_pwm.cpp b/Marlin/src/HAL/STM32/fast_pwm.cpp
new file mode 100644
index 0000000000..a0d8ecc612
--- /dev/null
+++ b/Marlin/src/HAL/STM32/fast_pwm.cpp
@@ -0,0 +1,88 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfig.h"
+
+// Array to support sticky frequency sets per timer
+static uint16_t timer_freq[TIMER_NUM];
+
+void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) {
+ const uint16_t duty = invert ? v_size - v : v;
+ if (PWM_PIN(pin)) {
+ const PinName pin_name = digitalPinToPinName(pin);
+ TIM_TypeDef * const Instance = (TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM);
+
+ const timer_index_t index = get_timer_index(Instance);
+ const bool needs_freq = (HardwareTimer_Handle[index] == nullptr);
+ if (needs_freq) // A new instance must be set to the default frequency of PWM_FREQUENCY
+ HardwareTimer_Handle[index]->__this = new HardwareTimer((TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM));
+
+ HardwareTimer * const HT = (HardwareTimer *)(HardwareTimer_Handle[index]->__this);
+ const uint32_t channel = STM_PIN_CHANNEL(pinmap_function(pin_name, PinMap_PWM));
+ const TimerModes_t previousMode = HT->getMode(channel);
+ if (previousMode != TIMER_OUTPUT_COMPARE_PWM1)
+ HT->setMode(channel, TIMER_OUTPUT_COMPARE_PWM1, pin);
+
+ if (needs_freq && timer_freq[index] == 0) // If the timer is unconfigured and no freq is set then default PWM_FREQUENCY
+ set_pwm_frequency(pin_name, PWM_FREQUENCY); // Set the frequency and save the value to the assigned index no.
+
+ // Note the resolution is sticky here, the input can be upto 16 bits and that would require RESOLUTION_16B_COMPARE_FORMAT (16)
+ // If such a need were to manifest then we would need to calc the resolution based on the v_size parameter and add code for it.
+ HT->setCaptureCompare(channel, duty, RESOLUTION_8B_COMPARE_FORMAT); // Set the duty, the calc is done in the library :)
+ pinmap_pinout(pin_name, PinMap_PWM); // Make sure the pin output state is set.
+ if (previousMode != TIMER_OUTPUT_COMPARE_PWM1) HT->resume();
+ }
+ else {
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, duty < v_size / 2 ? LOW : HIGH);
+ }
+}
+
+void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) {
+ if (!PWM_PIN(pin)) return; // Don't proceed if no hardware timer
+ const PinName pin_name = digitalPinToPinName(pin);
+ TIM_TypeDef * const Instance = (TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM); // Get HAL timer instance
+ const timer_index_t index = get_timer_index(Instance);
+
+ // Protect used timers.
+ #ifdef STEP_TIMER
+ if (index == TIMER_INDEX(STEP_TIMER)) return;
+ #endif
+ #ifdef TEMP_TIMER
+ if (index == TIMER_INDEX(TEMP_TIMER)) return;
+ #endif
+ #if defined(PULSE_TIMER) && MF_TIMER_PULSE != MF_TIMER_STEP
+ if (index == TIMER_INDEX(PULSE_TIMER)) return;
+ #endif
+
+ if (HardwareTimer_Handle[index] == nullptr) // If frequency is set before duty we need to create a handle here.
+ HardwareTimer_Handle[index]->__this = new HardwareTimer((TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM));
+ HardwareTimer * const HT = (HardwareTimer *)(HardwareTimer_Handle[index]->__this);
+ HT->setOverflow(f_desired, HERTZ_FORMAT);
+ timer_freq[index] = f_desired; // Save the last frequency so duty will not set the default for this timer number.
+}
+
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/fastio.cpp b/Marlin/src/HAL/STM32/fastio.cpp
new file mode 100644
index 0000000000..b34555b8c8
--- /dev/null
+++ b/Marlin/src/HAL/STM32/fastio.cpp
@@ -0,0 +1,36 @@
+/**
+ * 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
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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 "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfig.h"
+
+GPIO_TypeDef* FastIOPortMap[LastPort + 1] = { 0 };
+
+void FastIO_init() {
+ LOOP_L_N(i, NUM_DIGITAL_PINS)
+ FastIOPortMap[STM_PORT(digitalPin[i])] = get_GPIO_Port(STM_PORT(digitalPin[i]));
+}
+
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/fastio.h b/Marlin/src/HAL/STM32/fastio.h
new file mode 100644
index 0000000000..4a48954471
--- /dev/null
+++ b/Marlin/src/HAL/STM32/fastio.h
@@ -0,0 +1,91 @@
+/**
+ * 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
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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
+
+/**
+ * Fast I/O interfaces for STM32
+ * These use GPIO register access for fast port manipulation.
+ */
+
+// ------------------------
+// Public Variables
+// ------------------------
+
+extern GPIO_TypeDef * FastIOPortMap[];
+
+// ------------------------
+// Public functions
+// ------------------------
+
+void FastIO_init(); // Must be called before using fast io macros
+#define FASTIO_INIT() FastIO_init()
+
+// ------------------------
+// Defines
+// ------------------------
+
+#define _BV32(b) (1UL << (b))
+
+#ifndef PWM
+ #define PWM OUTPUT
+#endif
+
+#if defined(STM32F0xx) || defined(STM32F1xx) || defined(STM32F3xx) || defined(STM32L0xx) || defined(STM32L4xx)
+ #define _WRITE(IO, V) do { \
+ if (V) FastIOPortMap[STM_PORT(digitalPinToPinName(IO))]->BSRR = _BV32(STM_PIN(digitalPinToPinName(IO))) ; \
+ else FastIOPortMap[STM_PORT(digitalPinToPinName(IO))]->BRR = _BV32(STM_PIN(digitalPinToPinName(IO))) ; \
+ }while(0)
+#else
+ #define _WRITE(IO, V) (FastIOPortMap[STM_PORT(digitalPinToPinName(IO))]->BSRR = _BV32(STM_PIN(digitalPinToPinName(IO)) + ((V) ? 0 : 16)))
+#endif
+
+#define _READ(IO) bool(READ_BIT(FastIOPortMap[STM_PORT(digitalPinToPinName(IO))]->IDR, _BV32(STM_PIN(digitalPinToPinName(IO)))))
+#define _TOGGLE(IO) TBI32(FastIOPortMap[STM_PORT(digitalPinToPinName(IO))]->ODR, STM_PIN(digitalPinToPinName(IO)))
+
+#define _GET_MODE(IO)
+#define _SET_MODE(IO,M) pinMode(IO, M)
+#define _SET_OUTPUT(IO) pinMode(IO, OUTPUT) //!< Output Push Pull Mode & GPIO_NOPULL
+#define _SET_OUTPUT_OD(IO) pinMode(IO, OUTPUT_OPEN_DRAIN)
+
+#define WRITE(IO,V) _WRITE(IO,V)
+#define READ(IO) _READ(IO)
+#define TOGGLE(IO) _TOGGLE(IO)
+
+#define OUT_WRITE(IO,V) do{ _SET_OUTPUT(IO); WRITE(IO,V); }while(0)
+#define OUT_WRITE_OD(IO,V) do{ _SET_OUTPUT_OD(IO); WRITE(IO,V); }while(0)
+
+#define SET_INPUT(IO) _SET_MODE(IO, INPUT) //!< Input Floating Mode
+#define SET_INPUT_PULLUP(IO) _SET_MODE(IO, INPUT_PULLUP) //!< Input with Pull-up activation
+#define SET_INPUT_PULLDOWN(IO) _SET_MODE(IO, INPUT_PULLDOWN) //!< Input with Pull-down activation
+#define SET_OUTPUT(IO) OUT_WRITE(IO, LOW)
+#define SET_PWM(IO) _SET_MODE(IO, PWM)
+
+#define IS_INPUT(IO)
+#define IS_OUTPUT(IO)
+
+#define PWM_PIN(P) digitalPinHasPWM(P)
+#define NO_COMPILE_TIME_PWM
+
+// digitalRead/Write wrappers
+#define extDigitalRead(IO) digitalRead(IO)
+#define extDigitalWrite(IO,V) digitalWrite(IO,V)
diff --git a/Marlin/src/HAL/STM32/inc/Conditionals_LCD.h b/Marlin/src/HAL/STM32/inc/Conditionals_LCD.h
new file mode 100644
index 0000000000..5f1c4b1601
--- /dev/null
+++ b/Marlin/src/HAL/STM32/inc/Conditionals_LCD.h
@@ -0,0 +1,22 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
diff --git a/Marlin/src/HAL/STM32/inc/Conditionals_adv.h b/Marlin/src/HAL/STM32/inc/Conditionals_adv.h
new file mode 100644
index 0000000000..451c94f25d
--- /dev/null
+++ b/Marlin/src/HAL/STM32/inc/Conditionals_adv.h
@@ -0,0 +1,35 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#if BOTH(SDSUPPORT, USBD_USE_CDC_MSC) && DISABLED(NO_SD_HOST_DRIVE)
+ #define HAS_SD_HOST_DRIVE 1
+#endif
+
+// Fix F_CPU not being a compile-time constant in STSTM32 framework
+#ifdef BOARD_F_CPU
+ #undef F_CPU
+ #define F_CPU BOARD_F_CPU
+#endif
+
+// The Sensitive Pins array is not optimizable
+#define RUNTIME_ONLY_ANALOG_TO_DIGITAL
diff --git a/Marlin/src/HAL/STM32/inc/Conditionals_post.h b/Marlin/src/HAL/STM32/inc/Conditionals_post.h
new file mode 100644
index 0000000000..c5ce66a26f
--- /dev/null
+++ b/Marlin/src/HAL/STM32/inc/Conditionals_post.h
@@ -0,0 +1,34 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+// If no real or emulated EEPROM selected, fall back to SD emulation
+#if USE_FALLBACK_EEPROM
+ #define SDCARD_EEPROM_EMULATION
+#elif EITHER(I2C_EEPROM, SPI_EEPROM)
+ #define USE_SHARED_EEPROM 1
+#endif
+
+// Some STM32F4 boards may lose steps when saving to EEPROM during print (PR #17946)
+#if defined(STM32F4xx) && ENABLED(FLASH_EEPROM_EMULATION) && PRINTCOUNTER_SAVE_INTERVAL > 0
+ #define PRINTCOUNTER_SYNC 1
+#endif
diff --git a/Marlin/src/HAL/STM32/inc/SanityCheck.h b/Marlin/src/HAL/STM32/inc/SanityCheck.h
new file mode 100644
index 0000000000..e8ddfa1720
--- /dev/null
+++ b/Marlin/src/HAL/STM32/inc/SanityCheck.h
@@ -0,0 +1,111 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+/**
+ * Test STM32-specific configuration values for errors at compile-time.
+ */
+//#if ENABLED(SPINDLE_LASER_USE_PWM) && !(SPINDLE_LASER_PWM_PIN == 4 || SPINDLE_LASER_PWM_PIN == 6 || SPINDLE_LASER_PWM_PIN == 11)
+// #error "SPINDLE_LASER_PWM_PIN must use SERVO0, SERVO1 or SERVO3 connector"
+//#endif
+
+
+#if ENABLED(SDCARD_EEPROM_EMULATION) && DISABLED(SDSUPPORT)
+ #undef SDCARD_EEPROM_EMULATION // Avoid additional error noise
+ #if USE_FALLBACK_EEPROM
+ #warning "EEPROM type not specified. Fallback is SDCARD_EEPROM_EMULATION."
+ #endif
+ #error "SDCARD_EEPROM_EMULATION requires SDSUPPORT. Enable SDSUPPORT or choose another EEPROM emulation."
+#endif
+
+#if !defined(STM32F4xx) && ENABLED(FLASH_EEPROM_LEVELING)
+ #error "FLASH_EEPROM_LEVELING is currently only supported on STM32F4 hardware."
+#endif
+
+#if ENABLED(SERIAL_STATS_MAX_RX_QUEUED)
+ #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on STM32."
+#elif ENABLED(SERIAL_STATS_DROPPED_RX)
+ #error "SERIAL_STATS_DROPPED_RX is not supported on STM32."
+#endif
+
+#if ANY(TFT_COLOR_UI, TFT_LVGL_UI, TFT_CLASSIC_UI) && NOT_TARGET(STM32H7xx, STM32F4xx, STM32F1xx)
+ #error "TFT_COLOR_UI, TFT_LVGL_UI and TFT_CLASSIC_UI are currently only supported on STM32H7, STM32F4 and STM32F1 hardware."
+#endif
+
+/**
+ * Check for common serial pin conflicts
+ */
+#define _CHECK_SERIAL_PIN(N) (( \
+ BTN_EN1 == N || DOGLCD_CS == N || HEATER_BED_PIN == N || FAN_PIN == N || \
+ SDIO_D2_PIN == N || SDIO_D3_PIN == N || SDIO_CK_PIN == N || SDIO_CMD_PIN == N \
+ ))
+#define CHECK_SERIAL_PIN(T,N) defined(UART##N##_##T##_PIN) && _CHECK_SERIAL_PIN(UART##N##_##T##_PIN)
+#if SERIAL_IN_USE(1)
+ #if CHECK_SERIAL_PIN(TX,1)
+ #error "Serial Port 1 TX IO pins conflict with another pin on the board."
+ #endif
+ #if CHECK_SERIAL_PIN(RX,1)
+ #error "Serial Port 1 RX IO pins conflict with another pin on the board."
+ #endif
+#endif
+#if SERIAL_IN_USE(2)
+ #if CHECK_SERIAL_PIN(TX,2)
+ #error "Serial Port 2 TX IO pins conflict with another pin on the board."
+ #endif
+ #if CHECK_SERIAL_PIN(RX,2)
+ #error "Serial Port 2 RX IO pins conflict with another pin on the board."
+ #endif
+#endif
+#if SERIAL_IN_USE(3)
+ #if CHECK_SERIAL_PIN(TX,3)
+ #error "Serial Port 3 TX IO pins conflict with another pin on the board."
+ #endif
+ #if CHECK_SERIAL_PIN(RX,3)
+ #error "Serial Port 3 RX IO pins conflict with another pin on the board."
+ #endif
+#endif
+#if SERIAL_IN_USE(4)
+ #if CHECK_SERIAL_PIN(TX,4)
+ #error "Serial Port 4 TX IO pins conflict with another pin on the board."
+ #endif
+ #if CHECK_SERIAL_PIN(RX,4)
+ #error "Serial Port 4 RX IO pins conflict with another pin on the board."
+ #endif
+#endif
+#if SERIAL_IN_USE(5)
+ #if CHECK_SERIAL_PIN(TX,5)
+ #error "Serial Port 5 TX IO pins conflict with another pin on the board."
+ #endif
+ #if CHECK_SERIAL_PIN(RX,5)
+ #error "Serial Port 5 RX IO pins conflict with another pin on the board."
+ #endif
+#endif
+#if SERIAL_IN_USE(6)
+ #if CHECK_SERIAL_PIN(TX,6)
+ #error "Serial Port 6 TX IO pins conflict with another pin on the board."
+ #endif
+ #if CHECK_SERIAL_PIN(RX,6)
+ #error "Serial Port 6 RX IO pins conflict with another pin on the board."
+ #endif
+#endif
+#undef CHECK_SERIAL_PIN
+#undef _CHECK_SERIAL_PIN
diff --git a/Marlin/src/HAL/STM32/msc_sd.cpp b/Marlin/src/HAL/STM32/msc_sd.cpp
new file mode 100644
index 0000000000..a40bec9d64
--- /dev/null
+++ b/Marlin/src/HAL/STM32/msc_sd.cpp
@@ -0,0 +1,130 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2019 BigTreeTech [https://github.com/bigtreetech]
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#include "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if HAS_SD_HOST_DRIVE
+
+#include "../shared/Marduino.h"
+#include "msc_sd.h"
+#include "usbd_core.h"
+
+#include "../../sd/cardreader.h"
+
+#include
+#include
+
+#define BLOCK_SIZE 512
+#define PRODUCT_ID 0x29
+
+class Sd2CardUSBMscHandler : public USBMscHandler {
+public:
+ DiskIODriver* diskIODriver() {
+ #if ENABLED(MULTI_VOLUME)
+ #if SHARED_VOLUME_IS(SD_ONBOARD)
+ return &card.media_driver_sdcard;
+ #elif SHARED_VOLUME_IS(USB_FLASH_DRIVE)
+ return &card.media_driver_usbFlash;
+ #endif
+ #else
+ return card.diskIODriver();
+ #endif
+ }
+
+ bool GetCapacity(uint32_t *pBlockNum, uint16_t *pBlockSize) {
+ *pBlockNum = diskIODriver()->cardSize();
+ *pBlockSize = BLOCK_SIZE;
+ return true;
+ }
+
+ bool Write(uint8_t *pBuf, uint32_t blkAddr, uint16_t blkLen) {
+ auto sd2card = diskIODriver();
+ // single block
+ if (blkLen == 1) {
+ hal.watchdog_refresh();
+ sd2card->writeBlock(blkAddr, pBuf);
+ return true;
+ }
+
+ // multi block optimization
+ sd2card->writeStart(blkAddr, blkLen);
+ while (blkLen--) {
+ hal.watchdog_refresh();
+ sd2card->writeData(pBuf);
+ pBuf += BLOCK_SIZE;
+ }
+ sd2card->writeStop();
+ return true;
+ }
+
+ bool Read(uint8_t *pBuf, uint32_t blkAddr, uint16_t blkLen) {
+ auto sd2card = diskIODriver();
+ // single block
+ if (blkLen == 1) {
+ hal.watchdog_refresh();
+ sd2card->readBlock(blkAddr, pBuf);
+ return true;
+ }
+
+ // multi block optimization
+ sd2card->readStart(blkAddr);
+ while (blkLen--) {
+ hal.watchdog_refresh();
+ sd2card->readData(pBuf);
+ pBuf += BLOCK_SIZE;
+ }
+ sd2card->readStop();
+ return true;
+ }
+
+ bool IsReady() {
+ return diskIODriver()->isReady();
+ }
+};
+
+Sd2CardUSBMscHandler usbMscHandler;
+
+/* USB Mass storage Standard Inquiry Data */
+uint8_t Marlin_STORAGE_Inquirydata[] = { /* 36 */
+ /* LUN 0 */
+ 0x00,
+ 0x80,
+ 0x02,
+ 0x02,
+ (STANDARD_INQUIRY_DATA_LEN - 5),
+ 0x00,
+ 0x00,
+ 0x00,
+ 'M', 'A', 'R', 'L', 'I', 'N', ' ', ' ', /* Manufacturer : 8 bytes */
+ 'P', 'r', 'o', 'd', 'u', 'c', 't', ' ', /* Product : 16 Bytes */
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ '0', '.', '0', '1', /* Version : 4 Bytes */
+};
+
+USBMscHandler *pSingleMscHandler = &usbMscHandler;
+
+void MSC_SD_init() {
+ USBDevice.end();
+ delay(200);
+ USBDevice.registerMscHandlers(1, &pSingleMscHandler, Marlin_STORAGE_Inquirydata);
+ USBDevice.begin();
+}
+
+#endif // HAS_SD_HOST_DRIVE
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/msc_sd.h b/Marlin/src/HAL/STM32/msc_sd.h
new file mode 100644
index 0000000000..a8e5349f7c
--- /dev/null
+++ b/Marlin/src/HAL/STM32/msc_sd.h
@@ -0,0 +1,18 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2019 BigTreeTech [https://github.com/bigtreetech]
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#pragma once
+
+void MSC_SD_init();
diff --git a/Marlin/src/HAL/STM32/pinsDebug.h b/Marlin/src/HAL/STM32/pinsDebug.h
new file mode 100644
index 0000000000..29a4e003f9
--- /dev/null
+++ b/Marlin/src/HAL/STM32/pinsDebug.h
@@ -0,0 +1,287 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include
+
+#ifndef NUM_DIGITAL_PINS
+ // Only in ST's Arduino core (STM32duino, STM32Core)
+ #error "Expected NUM_DIGITAL_PINS not found"
+#endif
+
+/**
+ * Life gets complicated if you want an easy to use 'M43 I' output (in port/pin order)
+ * because the variants in this platform do not always define all the I/O port/pins
+ * that a CPU has.
+ *
+ * VARIABLES:
+ * Ard_num - Arduino pin number - defined by the platform. It is used by digitalRead and
+ * digitalWrite commands and by M42.
+ * - does not contain port/pin info
+ * - is not in port/pin order
+ * - typically a variant will only assign Ard_num to port/pins that are actually used
+ * Index - M43 counter - only used to get Ard_num
+ * x - a parameter/argument used to search the pin_array to try to find a signal name
+ * associated with a Ard_num
+ * Port_pin - port number and pin number for use with CPU registers and printing reports
+ *
+ * Since M43 uses digitalRead and digitalWrite commands, only the Port_pins with an Ard_num
+ * are accessed and/or displayed.
+ *
+ * Three arrays are used.
+ *
+ * digitalPin[] is provided by the platform. It consists of the Port_pin numbers in
+ * Arduino pin number order.
+ *
+ * pin_array is a structure generated by the pins/pinsDebug.h header file. It is generated by
+ * the preprocessor. Only the signals associated with enabled options are in this table.
+ * It contains:
+ * - name of the signal
+ * - the Ard_num assigned by the pins_YOUR_BOARD.h file using the platform defines.
+ * EXAMPLE: "#define KILL_PIN PB1" results in Ard_num of 57. 57 is then used as the
+ * argument to digitalPinToPinName(IO) to get the Port_pin number
+ * - if it is a digital or analog signal. PWMs are considered digital here.
+ *
+ * pin_xref is a structure generated by this header file. It is generated by the
+ * preprocessor. It is in port/pin order. It contains just the port/pin numbers defined by the
+ * platform for this variant.
+ * - Ard_num
+ * - printable version of Port_pin
+ *
+ * Routines with an "x" as a parameter/argument are used to search the pin_array to try to
+ * find a signal name associated with a port/pin.
+ *
+ * NOTE - the Arduino pin number is what is used by the M42 command, NOT the port/pin for that
+ * signal. The Arduino pin number is listed by the M43 I command.
+ */
+
+////////////////////////////////////////////////////////
+//
+// make a list of the Arduino pin numbers in the Port/Pin order
+//
+
+#define _PIN_ADD(NAME_ALPHA, ARDUINO_NUM) { NAME_ALPHA, ARDUINO_NUM },
+#define PIN_ADD(NAME) _PIN_ADD(#NAME, NAME)
+
+typedef struct {
+ char Port_pin_alpha[5];
+ pin_t Ard_num;
+} XrefInfo;
+
+const XrefInfo pin_xref[] PROGMEM = {
+ #include "pins_Xref.h"
+};
+
+////////////////////////////////////////////////////////////
+
+#define MODE_PIN_INPUT 0 // Input mode (reset state)
+#define MODE_PIN_OUTPUT 1 // General purpose output mode
+#define MODE_PIN_ALT 2 // Alternate function mode
+#define MODE_PIN_ANALOG 3 // Analog mode
+
+#define PIN_NUM(P) (P & 0x000F)
+#define PIN_NUM_ALPHA_LEFT(P) (((P & 0x000F) < 10) ? ('0' + (P & 0x000F)) : '1')
+#define PIN_NUM_ALPHA_RIGHT(P) (((P & 0x000F) > 9) ? ('0' + (P & 0x000F) - 10) : 0 )
+#define PORT_NUM(P) ((P >> 4) & 0x0007)
+#define PORT_ALPHA(P) ('A' + (P >> 4))
+
+/**
+ * Translation of routines & variables used by pinsDebug.h
+ */
+
+#if NUM_ANALOG_FIRST >= NUM_DIGITAL_PINS
+ #define HAS_HIGH_ANALOG_PINS 1
+#endif
+#define NUM_ANALOG_LAST ((NUM_ANALOG_FIRST) + (NUM_ANALOG_INPUTS) - 1)
+#define NUMBER_PINS_TOTAL ((NUM_DIGITAL_PINS) + TERN0(HAS_HIGH_ANALOG_PINS, NUM_ANALOG_INPUTS))
+#define VALID_PIN(P) (WITHIN(P, 0, (NUM_DIGITAL_PINS) - 1) || TERN0(HAS_HIGH_ANALOG_PINS, WITHIN(P, NUM_ANALOG_FIRST, NUM_ANALOG_LAST)))
+#define digitalRead_mod(Ard_num) extDigitalRead(Ard_num) // must use Arduino pin numbers when doing reads
+#define PRINT_PIN(Q)
+#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0)
+#define PRINT_PORT(ANUM) port_print(ANUM)
+#define DIGITAL_PIN_TO_ANALOG_PIN(ANUM) -1 // will report analog pin number in the print port routine
+
+// x is a variable used to search pin_array
+#define GET_ARRAY_IS_DIGITAL(x) ((bool) pin_array[x].is_digital)
+#define GET_ARRAY_PIN(x) ((pin_t) pin_array[x].pin)
+#define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0)
+#define MULTI_NAME_PAD 33 // space needed to be pretty if not first name assigned to a pin
+
+//
+// Pin Mapping for M43
+//
+#define GET_PIN_MAP_PIN_M43(Index) pin_xref[Index].Ard_num
+
+#ifndef M43_NEVER_TOUCH
+ #define _M43_NEVER_TOUCH(Index) (Index >= 9 && Index <= 12) // SERIAL/USB pins: PA9(TX) PA10(RX) PA11(USB_DM) PA12(USB_DP)
+ #ifdef KILL_PIN
+ #define M43_NEVER_TOUCH(Index) m43_never_touch(Index)
+
+ bool m43_never_touch(const pin_t Index) {
+ static pin_t M43_kill_index = -1;
+ if (M43_kill_index < 0)
+ for (M43_kill_index = 0; M43_kill_index < NUMBER_PINS_TOTAL; M43_kill_index++)
+ if (KILL_PIN == GET_PIN_MAP_PIN_M43(M43_kill_index)) break;
+ return _M43_NEVER_TOUCH(Index) || Index == M43_kill_index; // KILL_PIN and SERIAL/USB
+ }
+ #else
+ #define M43_NEVER_TOUCH(Index) _M43_NEVER_TOUCH(Index)
+ #endif
+#endif
+
+uint8_t get_pin_mode(const pin_t Ard_num) {
+ const PinName dp = digitalPinToPinName(Ard_num);
+ uint32_t ll_pin = STM_LL_GPIO_PIN(dp);
+ GPIO_TypeDef *port = get_GPIO_Port(STM_PORT(dp));
+ uint32_t mode = LL_GPIO_GetPinMode(port, ll_pin);
+ switch (mode) {
+ case LL_GPIO_MODE_ANALOG: return MODE_PIN_ANALOG;
+ case LL_GPIO_MODE_INPUT: return MODE_PIN_INPUT;
+ case LL_GPIO_MODE_OUTPUT: return MODE_PIN_OUTPUT;
+ case LL_GPIO_MODE_ALTERNATE: return MODE_PIN_ALT;
+ TERN_(STM32F1xx, case LL_GPIO_MODE_FLOATING:)
+ default: return 0;
+ }
+}
+
+bool GET_PINMODE(const pin_t Ard_num) {
+ const uint8_t pin_mode = get_pin_mode(Ard_num);
+ return pin_mode == MODE_PIN_OUTPUT || pin_mode == MODE_PIN_ALT; // assume all alt definitions are PWM
+}
+
+int8_t digital_pin_to_analog_pin(const pin_t Ard_num) {
+ if (WITHIN(Ard_num, NUM_ANALOG_FIRST, NUM_ANALOG_LAST))
+ return Ard_num - NUM_ANALOG_FIRST;
+
+ const uint32_t ind = digitalPinToAnalogInput(Ard_num);
+ return (ind < NUM_ANALOG_INPUTS) ? ind : -1;
+}
+
+bool IS_ANALOG(const pin_t Ard_num) {
+ return get_pin_mode(Ard_num) == MODE_PIN_ANALOG;
+}
+
+bool is_digital(const pin_t Ard_num) {
+ const uint8_t pin_mode = get_pin_mode(pin_array[Ard_num].pin);
+ return pin_mode == MODE_PIN_INPUT || pin_mode == MODE_PIN_OUTPUT;
+}
+
+void port_print(const pin_t Ard_num) {
+ char buffer[16];
+ pin_t Index;
+ for (Index = 0; Index < NUMBER_PINS_TOTAL; Index++)
+ if (Ard_num == GET_PIN_MAP_PIN_M43(Index)) break;
+
+ const char * ppa = pin_xref[Index].Port_pin_alpha;
+ sprintf_P(buffer, PSTR("%s"), ppa);
+ SERIAL_ECHO(buffer);
+ if (ppa[3] == '\0') SERIAL_CHAR(' ');
+
+ // print analog pin number
+ const int8_t Port_pin = digital_pin_to_analog_pin(Ard_num);
+ if (Port_pin >= 0) {
+ sprintf_P(buffer, PSTR(" (A%d) "), Port_pin);
+ SERIAL_ECHO(buffer);
+ if (Port_pin < 10) SERIAL_CHAR(' ');
+ }
+ else
+ SERIAL_ECHO_SP(7);
+
+ // Print number to be used with M42
+ int calc_p = Ard_num;
+ if (Ard_num > NUM_DIGITAL_PINS) {
+ calc_p -= NUM_ANALOG_FIRST;
+ if (calc_p > 7) calc_p += 8;
+ }
+ SERIAL_ECHOPGM(" M42 P", calc_p);
+ SERIAL_CHAR(' ');
+ if (calc_p < 100) {
+ SERIAL_CHAR(' ');
+ if (calc_p < 10)
+ SERIAL_CHAR(' ');
+ }
+}
+
+bool pwm_status(const pin_t Ard_num) {
+ return get_pin_mode(Ard_num) == MODE_PIN_ALT;
+}
+
+void pwm_details(const pin_t Ard_num) {
+ #ifndef STM32F1xx
+ if (pwm_status(Ard_num)) {
+ uint32_t alt_all = 0;
+ const PinName dp = digitalPinToPinName(Ard_num);
+ pin_t pin_number = uint8_t(PIN_NUM(dp));
+ const bool over_7 = pin_number >= 8;
+ const uint8_t ind = over_7 ? 1 : 0;
+ switch (PORT_ALPHA(dp)) { // get alt function
+ case 'A' : alt_all = GPIOA->AFR[ind]; break;
+ case 'B' : alt_all = GPIOB->AFR[ind]; break;
+ case 'C' : alt_all = GPIOC->AFR[ind]; break;
+ case 'D' : alt_all = GPIOD->AFR[ind]; break;
+ #ifdef PE_0
+ case 'E' : alt_all = GPIOE->AFR[ind]; break;
+ #elif defined(PF_0)
+ case 'F' : alt_all = GPIOF->AFR[ind]; break;
+ #elif defined(PG_0)
+ case 'G' : alt_all = GPIOG->AFR[ind]; break;
+ #elif defined(PH_0)
+ case 'H' : alt_all = GPIOH->AFR[ind]; break;
+ #elif defined(PI_0)
+ case 'I' : alt_all = GPIOI->AFR[ind]; break;
+ #elif defined(PJ_0)
+ case 'J' : alt_all = GPIOJ->AFR[ind]; break;
+ #elif defined(PK_0)
+ case 'K' : alt_all = GPIOK->AFR[ind]; break;
+ #elif defined(PL_0)
+ case 'L' : alt_all = GPIOL->AFR[ind]; break;
+ #endif
+ }
+ if (over_7) pin_number -= 8;
+
+ uint8_t alt_func = (alt_all >> (4 * pin_number)) & 0x0F;
+ SERIAL_ECHOPGM("Alt Function: ", alt_func);
+ if (alt_func < 10) SERIAL_CHAR(' ');
+ SERIAL_ECHOPGM(" - ");
+ switch (alt_func) {
+ case 0 : SERIAL_ECHOPGM("system (misc. I/O)"); break;
+ case 1 : SERIAL_ECHOPGM("TIM1/TIM2 (probably PWM)"); break;
+ case 2 : SERIAL_ECHOPGM("TIM3..5 (probably PWM)"); break;
+ case 3 : SERIAL_ECHOPGM("TIM8..11 (probably PWM)"); break;
+ case 4 : SERIAL_ECHOPGM("I2C1..3"); break;
+ case 5 : SERIAL_ECHOPGM("SPI1/SPI2"); break;
+ case 6 : SERIAL_ECHOPGM("SPI3"); break;
+ case 7 : SERIAL_ECHOPGM("USART1..3"); break;
+ case 8 : SERIAL_ECHOPGM("USART4..6"); break;
+ case 9 : SERIAL_ECHOPGM("CAN1/CAN2, TIM12..14 (probably PWM)"); break;
+ case 10 : SERIAL_ECHOPGM("OTG"); break;
+ case 11 : SERIAL_ECHOPGM("ETH"); break;
+ case 12 : SERIAL_ECHOPGM("FSMC, SDIO, OTG"); break;
+ case 13 : SERIAL_ECHOPGM("DCMI"); break;
+ case 14 : SERIAL_ECHOPGM("unused (shouldn't see this)"); break;
+ case 15 : SERIAL_ECHOPGM("EVENTOUT"); break;
+ }
+ }
+ #else
+ // TODO: F1 doesn't support changing pins function, so we need to check the function of the PIN and if it's enabled
+ #endif
+} // pwm_details
diff --git a/Marlin/src/HAL/STM32/pins_Xref.h b/Marlin/src/HAL/STM32/pins_Xref.h
new file mode 100644
index 0000000000..890e561860
--- /dev/null
+++ b/Marlin/src/HAL/STM32/pins_Xref.h
@@ -0,0 +1,612 @@
+/**
+ * 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 .
+ *
+ */
+
+//
+// make a list of the Arduino pin numbers in the Port/Pin order
+//
+#ifdef PA0
+ PIN_ADD(PA0)
+#endif
+#ifdef PA1
+ PIN_ADD(PA1)
+#endif
+#ifdef PA2
+ PIN_ADD(PA2)
+#endif
+#ifdef PA3
+ PIN_ADD(PA3)
+#endif
+#ifdef PA4
+ PIN_ADD(PA4)
+#endif
+#ifdef PA5
+ PIN_ADD(PA5)
+#endif
+#ifdef PA6
+ PIN_ADD(PA6)
+#endif
+#ifdef PA7
+ PIN_ADD(PA7)
+#endif
+#ifdef PA8
+ PIN_ADD(PA8)
+#endif
+#ifdef PA9
+ PIN_ADD(PA9)
+#endif
+#ifdef PA10
+ PIN_ADD(PA10)
+#endif
+#ifdef PA11
+ PIN_ADD(PA11)
+#endif
+#ifdef PA12
+ PIN_ADD(PA12)
+#endif
+#ifdef PA13
+ PIN_ADD(PA13)
+#endif
+#ifdef PA14
+ PIN_ADD(PA14)
+#endif
+#ifdef PA15
+ PIN_ADD(PA15)
+#endif
+
+#ifdef PB0
+ PIN_ADD(PB0)
+#endif
+#ifdef PB1
+ PIN_ADD(PB1)
+#endif
+#ifdef PB2
+ PIN_ADD(PB2)
+#endif
+#ifdef PB3
+ PIN_ADD(PB3)
+#endif
+#ifdef PB4
+ PIN_ADD(PB4)
+#endif
+#ifdef PB5
+ PIN_ADD(PB5)
+#endif
+#ifdef PB6
+ PIN_ADD(PB6)
+#endif
+#ifdef PB7
+ PIN_ADD(PB7)
+#endif
+#ifdef PB8
+ PIN_ADD(PB8)
+#endif
+#ifdef PB9
+ PIN_ADD(PB9)
+#endif
+#ifdef PB10
+ PIN_ADD(PB10)
+#endif
+#ifdef PB11
+ PIN_ADD(PB11)
+#endif
+#ifdef PB12
+ PIN_ADD(PB12)
+#endif
+#ifdef PB13
+ PIN_ADD(PB13)
+#endif
+#ifdef PB14
+ PIN_ADD(PB14)
+#endif
+#ifdef PB15
+ PIN_ADD(PB15)
+#endif
+
+#ifdef PC0
+ PIN_ADD(PC0)
+#endif
+#ifdef PC1
+ PIN_ADD(PC1)
+#endif
+#ifdef PC2
+ PIN_ADD(PC2)
+#endif
+#ifdef PC3
+ PIN_ADD(PC3)
+#endif
+#ifdef PC4
+ PIN_ADD(PC4)
+#endif
+#ifdef PC5
+ PIN_ADD(PC5)
+#endif
+#ifdef PC6
+ PIN_ADD(PC6)
+#endif
+#ifdef PC7
+ PIN_ADD(PC7)
+#endif
+#ifdef PC8
+ PIN_ADD(PC8)
+#endif
+#ifdef PC9
+ PIN_ADD(PC9)
+#endif
+#ifdef PC10
+ PIN_ADD(PC10)
+#endif
+#ifdef PC11
+ PIN_ADD(PC11)
+#endif
+#ifdef PC12
+ PIN_ADD(PC12)
+#endif
+#ifdef PC13
+ PIN_ADD(PC13)
+#endif
+#ifdef PC14
+ PIN_ADD(PC14)
+#endif
+#ifdef PC15
+ PIN_ADD(PC15)
+#endif
+
+#ifdef PD0
+ PIN_ADD(PD0)
+#endif
+#ifdef PD1
+ PIN_ADD(PD1)
+#endif
+#ifdef PD2
+ PIN_ADD(PD2)
+#endif
+#ifdef PD3
+ PIN_ADD(PD3)
+#endif
+#ifdef PD4
+ PIN_ADD(PD4)
+#endif
+#ifdef PD5
+ PIN_ADD(PD5)
+#endif
+#ifdef PD6
+ PIN_ADD(PD6)
+#endif
+#ifdef PD7
+ PIN_ADD(PD7)
+#endif
+#ifdef PD8
+ PIN_ADD(PD8)
+#endif
+#ifdef PD9
+ PIN_ADD(PD9)
+#endif
+#ifdef PD10
+ PIN_ADD(PD10)
+#endif
+#ifdef PD11
+ PIN_ADD(PD11)
+#endif
+#ifdef PD12
+ PIN_ADD(PD12)
+#endif
+#ifdef PD13
+ PIN_ADD(PD13)
+#endif
+#ifdef PD14
+ PIN_ADD(PD14)
+#endif
+#ifdef PD15
+ PIN_ADD(PD15)
+#endif
+
+#ifdef PE0
+ PIN_ADD(PE0)
+#endif
+#ifdef PE1
+ PIN_ADD(PE1)
+#endif
+#ifdef PE2
+ PIN_ADD(PE2)
+#endif
+#ifdef PE3
+ PIN_ADD(PE3)
+#endif
+#ifdef PE4
+ PIN_ADD(PE4)
+#endif
+#ifdef PE5
+ PIN_ADD(PE5)
+#endif
+#ifdef PE6
+ PIN_ADD(PE6)
+#endif
+#ifdef PE7
+ PIN_ADD(PE7)
+#endif
+#ifdef PE8
+ PIN_ADD(PE8)
+#endif
+#ifdef PE9
+ PIN_ADD(PE9)
+#endif
+#ifdef PE10
+ PIN_ADD(PE10)
+#endif
+#ifdef PE11
+ PIN_ADD(PE11)
+#endif
+#ifdef PE12
+ PIN_ADD(PE12)
+#endif
+#ifdef PE13
+ PIN_ADD(PE13)
+#endif
+#ifdef PE14
+ PIN_ADD(PE14)
+#endif
+#ifdef PE15
+ PIN_ADD(PE15)
+#endif
+
+#ifdef PF0
+ PIN_ADD(PF0)
+#endif
+#ifdef PF1
+ PIN_ADD(PF1)
+#endif
+#ifdef PF2
+ PIN_ADD(PF2)
+#endif
+#ifdef PF3
+ PIN_ADD(PF3)
+#endif
+#ifdef PF4
+ PIN_ADD(PF4)
+#endif
+#ifdef PF5
+ PIN_ADD(PF5)
+#endif
+#ifdef PF6
+ PIN_ADD(PF6)
+#endif
+#ifdef PF7
+ PIN_ADD(PF7)
+#endif
+#ifdef PF8
+ PIN_ADD(PF8)
+#endif
+#ifdef PF9
+ PIN_ADD(PF9)
+#endif
+#ifdef PF10
+ PIN_ADD(PF10)
+#endif
+#ifdef PF11
+ PIN_ADD(PF11)
+#endif
+#ifdef PF12
+ PIN_ADD(PF12)
+#endif
+#ifdef PF13
+ PIN_ADD(PF13)
+#endif
+#ifdef PF14
+ PIN_ADD(PF14)
+#endif
+#ifdef PF15
+ PIN_ADD(PF15)
+#endif
+
+#ifdef PG0
+ PIN_ADD(PG0)
+#endif
+#ifdef PG1
+ PIN_ADD(PG1)
+#endif
+#ifdef PG2
+ PIN_ADD(PG2)
+#endif
+#ifdef PG3
+ PIN_ADD(PG3)
+#endif
+#ifdef PG4
+ PIN_ADD(PG4)
+#endif
+#ifdef PG5
+ PIN_ADD(PG5)
+#endif
+#ifdef PG6
+ PIN_ADD(PG6)
+#endif
+#ifdef PG7
+ PIN_ADD(PG7)
+#endif
+#ifdef PG8
+ PIN_ADD(PG8)
+#endif
+#ifdef PG9
+ PIN_ADD(PG9)
+#endif
+#ifdef PG10
+ PIN_ADD(PG10)
+#endif
+#ifdef PG11
+ PIN_ADD(PG11)
+#endif
+#ifdef PG12
+ PIN_ADD(PG12)
+#endif
+#ifdef PG13
+ PIN_ADD(PG13)
+#endif
+#ifdef PG14
+ PIN_ADD(PG14)
+#endif
+#ifdef PG15
+ PIN_ADD(PG15)
+#endif
+
+#ifdef PH0
+ PIN_ADD(PH0)
+#endif
+#ifdef PH1
+ PIN_ADD(PH1)
+#endif
+#ifdef PH2
+ PIN_ADD(PH2)
+#endif
+#ifdef PH3
+ PIN_ADD(PH3)
+#endif
+#ifdef PH4
+ PIN_ADD(PH4)
+#endif
+#ifdef PH5
+ PIN_ADD(PH5)
+#endif
+#ifdef PH6
+ PIN_ADD(PH6)
+#endif
+#ifdef PH7
+ PIN_ADD(PH7)
+#endif
+#ifdef PH8
+ PIN_ADD(PH8)
+#endif
+#ifdef PH9
+ PIN_ADD(PH9)
+#endif
+#ifdef PH10
+ PIN_ADD(PH10)
+#endif
+#ifdef PH11
+ PIN_ADD(PH11)
+#endif
+#ifdef PH12
+ PIN_ADD(PH12)
+#endif
+#ifdef PH13
+ PIN_ADD(PH13)
+#endif
+#ifdef PH14
+ PIN_ADD(PH14)
+#endif
+#ifdef PH15
+ PIN_ADD(PH15)
+#endif
+
+#ifdef PI0
+ PIN_ADD(PI0)
+#endif
+#ifdef PI1
+ PIN_ADD(PI1)
+#endif
+#ifdef PI2
+ PIN_ADD(PI2)
+#endif
+#ifdef PI3
+ PIN_ADD(PI3)
+#endif
+#ifdef PI4
+ PIN_ADD(PI4)
+#endif
+#ifdef PI5
+ PIN_ADD(PI5)
+#endif
+#ifdef PI6
+ PIN_ADD(PI6)
+#endif
+#ifdef PI7
+ PIN_ADD(PI7)
+#endif
+#ifdef PI8
+ PIN_ADD(PI8)
+#endif
+#ifdef PI9
+ PIN_ADD(PI9)
+#endif
+#ifdef PI10
+ PIN_ADD(PI10)
+#endif
+#ifdef PI11
+ PIN_ADD(PI11)
+#endif
+#ifdef PI12
+ PIN_ADD(PI12)
+#endif
+#ifdef PI13
+ PIN_ADD(PI13)
+#endif
+#ifdef PI14
+ PIN_ADD(PI14)
+#endif
+#ifdef PI15
+ PIN_ADD(PI15)
+#endif
+
+#ifdef PJ0
+ PIN_ADD(PJ0)
+#endif
+#ifdef PJ1
+ PIN_ADD(PJ1)
+#endif
+#ifdef PJ2
+ PIN_ADD(PJ2)
+#endif
+#ifdef PJ3
+ PIN_ADD(PJ3)
+#endif
+#ifdef PJ4
+ PIN_ADD(PJ4)
+#endif
+#ifdef PJ5
+ PIN_ADD(PJ5)
+#endif
+#ifdef PJ6
+ PIN_ADD(PJ6)
+#endif
+#ifdef PJ7
+ PIN_ADD(PJ7)
+#endif
+#ifdef PJ8
+ PIN_ADD(PJ8)
+#endif
+#ifdef PJ9
+ PIN_ADD(PJ9)
+#endif
+#ifdef PJ10
+ PIN_ADD(PJ10)
+#endif
+#ifdef PJ11
+ PIN_ADD(PJ11)
+#endif
+#ifdef PJ12
+ PIN_ADD(PJ12)
+#endif
+#ifdef PJ13
+ PIN_ADD(PJ13)
+#endif
+#ifdef PJ14
+ PIN_ADD(PJ14)
+#endif
+#ifdef PJ15
+ PIN_ADD(PJ15)
+#endif
+
+#ifdef PK0
+ PIN_ADD(PK0)
+#endif
+#ifdef PK1
+ PIN_ADD(PK1)
+#endif
+#ifdef PK2
+ PIN_ADD(PK2)
+#endif
+#ifdef PK3
+ PIN_ADD(PK3)
+#endif
+#ifdef PK4
+ PIN_ADD(PK4)
+#endif
+#ifdef PK5
+ PIN_ADD(PK5)
+#endif
+#ifdef PK6
+ PIN_ADD(PK6)
+#endif
+#ifdef PK7
+ PIN_ADD(PK7)
+#endif
+#ifdef PK8
+ PIN_ADD(PK8)
+#endif
+#ifdef PK9
+ PIN_ADD(PK9)
+#endif
+#ifdef PK10
+ PIN_ADD(PK10)
+#endif
+#ifdef PK11
+ PIN_ADD(PK11)
+#endif
+#ifdef PK12
+ PIN_ADD(PK12)
+#endif
+#ifdef PK13
+ PIN_ADD(PK13)
+#endif
+#ifdef PK14
+ PIN_ADD(PK14)
+#endif
+#ifdef PK15
+ PIN_ADD(PK15)
+#endif
+
+#ifdef PL0
+ PIN_ADD(PL0)
+#endif
+#ifdef PL1
+ PIN_ADD(PL1)
+#endif
+#ifdef PL2
+ PIN_ADD(PL2)
+#endif
+#ifdef PL3
+ PIN_ADD(PL3)
+#endif
+#ifdef PL4
+ PIN_ADD(PL4)
+#endif
+#ifdef PL5
+ PIN_ADD(PL5)
+#endif
+#ifdef PL6
+ PIN_ADD(PL6)
+#endif
+#ifdef PL7
+ PIN_ADD(PL7)
+#endif
+#ifdef PL8
+ PIN_ADD(PL8)
+#endif
+#ifdef PL9
+ PIN_ADD(PL9)
+#endif
+#ifdef PL10
+ PIN_ADD(PL10)
+#endif
+#ifdef PL11
+ PIN_ADD(PL11)
+#endif
+#ifdef PL12
+ PIN_ADD(PL12)
+#endif
+#ifdef PL13
+ PIN_ADD(PL13)
+#endif
+#ifdef PL14
+ PIN_ADD(PL14)
+#endif
+#ifdef PL15
+ PIN_ADD(PL15)
+#endif
diff --git a/Marlin/src/HAL/STM32/sdio.cpp b/Marlin/src/HAL/STM32/sdio.cpp
new file mode 100644
index 0000000000..72518ef1cc
--- /dev/null
+++ b/Marlin/src/HAL/STM32/sdio.cpp
@@ -0,0 +1,457 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(SDIO_SUPPORT)
+
+#include "sdio.h"
+
+#include
+#include
+
+#if defined(STM32F103xE) || defined(STM32F103xG)
+ #include
+ #include
+#elif defined(STM32F4xx)
+ #include
+ #include
+ #include
+ #include
+#elif defined(STM32F7xx)
+ #include
+ #include
+ #include
+ #include
+#elif defined(STM32H7xx)
+ #define SDIO_FOR_STM32H7
+ #include
+ #include
+ #include
+ #include
+#else
+ #error "SDIO is only supported with STM32F103xE, STM32F103xG, STM32F4xx, STM32F7xx, and STM32H7xx."
+#endif
+
+// SDIO Max Clock (naming from STM Manual, don't change)
+#define SDIOCLK 48000000
+
+// Target Clock, configurable. Default is 18MHz, from STM32F1
+#ifndef SDIO_CLOCK
+ #define SDIO_CLOCK 18000000 // 18 MHz
+#endif
+
+SD_HandleTypeDef hsd; // SDIO structure
+
+static uint32_t clock_to_divider(uint32_t clk) {
+ #ifdef SDIO_FOR_STM32H7
+ // SDMMC_CK frequency = sdmmc_ker_ck / [2 * CLKDIV].
+ uint32_t sdmmc_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC);
+ return sdmmc_clk / (2U * SDIO_CLOCK) + (sdmmc_clk % (2U * SDIO_CLOCK) != 0);
+ #else
+ // limit the SDIO master clock to 8/3 of PCLK2. See STM32 Manuals
+ // Also limited to no more than 48Mhz (SDIOCLK).
+ const uint32_t pclk2 = HAL_RCC_GetPCLK2Freq();
+ clk = min(clk, (uint32_t)(pclk2 * 8 / 3));
+ clk = min(clk, (uint32_t)SDIOCLK);
+ // Round up divider, so we don't run the card over the speed supported,
+ // and subtract by 2, because STM32 will add 2, as written in the manual:
+ // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2]
+ return pclk2 / clk + (pclk2 % clk != 0) - 2;
+ #endif
+}
+
+// Start the SDIO clock
+void HAL_SD_MspInit(SD_HandleTypeDef *hsd) {
+ UNUSED(hsd);
+ #ifdef SDIO_FOR_STM32H7
+ pinmap_pinout(PC_12, PinMap_SD);
+ pinmap_pinout(PD_2, PinMap_SD);
+ pinmap_pinout(PC_8, PinMap_SD);
+ #if PINS_EXIST(SDIO_D1, SDIO_D2, SDIO_D3) // Define D1-D3 only for 4-bit wide SDIO bus
+ pinmap_pinout(PC_9, PinMap_SD);
+ pinmap_pinout(PC_10, PinMap_SD);
+ pinmap_pinout(PC_11, PinMap_SD);
+ #endif
+ __HAL_RCC_SDMMC1_CLK_ENABLE();
+ HAL_NVIC_EnableIRQ(SDMMC1_IRQn);
+ #else
+ __HAL_RCC_SDIO_CLK_ENABLE();
+ #endif
+}
+
+#ifdef SDIO_FOR_STM32H7
+
+ #define SD_TIMEOUT 1000 // ms
+
+ extern "C" void SDMMC1_IRQHandler(void) { HAL_SD_IRQHandler(&hsd); }
+
+ uint8_t waitingRxCplt = 0, waitingTxCplt = 0;
+ void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsdio) { waitingTxCplt = 0; }
+ void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsdio) { waitingRxCplt = 0; }
+
+ void HAL_SD_MspDeInit(SD_HandleTypeDef *hsd) {
+ __HAL_RCC_SDMMC1_FORCE_RESET(); delay(10);
+ __HAL_RCC_SDMMC1_RELEASE_RESET(); delay(10);
+ }
+
+ bool SDIO_Init() {
+ HAL_StatusTypeDef sd_state = HAL_OK;
+ if (hsd.Instance == SDMMC1) HAL_SD_DeInit(&hsd);
+
+ // HAL SD initialization
+ hsd.Instance = SDMMC1;
+ hsd.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
+ hsd.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
+ hsd.Init.BusWide = SDMMC_BUS_WIDE_1B;
+ hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
+ hsd.Init.ClockDiv = clock_to_divider(SDIO_CLOCK);
+ sd_state = HAL_SD_Init(&hsd);
+
+ #if PINS_EXIST(SDIO_D1, SDIO_D2, SDIO_D3)
+ if (sd_state == HAL_OK)
+ sd_state = HAL_SD_ConfigWideBusOperation(&hsd, SDMMC_BUS_WIDE_4B);
+ #endif
+
+ return (sd_state == HAL_OK);
+ }
+
+#else // !SDIO_FOR_STM32H7
+
+ #define SD_TIMEOUT 500 // ms
+
+ // SDIO retries, configurable. Default is 3, from STM32F1
+ #ifndef SDIO_READ_RETRIES
+ #define SDIO_READ_RETRIES 3
+ #endif
+
+ // F4 supports one DMA for RX and another for TX, but Marlin will never
+ // do read and write at same time, so we use the same DMA for both.
+ DMA_HandleTypeDef hdma_sdio;
+
+ #ifdef STM32F1xx
+ #define DMA_IRQ_HANDLER DMA2_Channel4_5_IRQHandler
+ #elif defined(STM32F4xx)
+ #define DMA_IRQ_HANDLER DMA2_Stream3_IRQHandler
+ #else
+ #error "Unknown STM32 architecture."
+ #endif
+
+ extern "C" void SDIO_IRQHandler(void) { HAL_SD_IRQHandler(&hsd); }
+ extern "C" void DMA_IRQ_HANDLER(void) { HAL_DMA_IRQHandler(&hdma_sdio); }
+
+ /*
+ SDIO_INIT_CLK_DIV is 118
+ SDIO clock frequency is 48MHz / (TRANSFER_CLOCK_DIV + 2)
+ SDIO init clock frequency should not exceed 400kHz = 48MHz / (118 + 2)
+
+ Default TRANSFER_CLOCK_DIV is 2 (118 / 40)
+ Default SDIO clock frequency is 48MHz / (2 + 2) = 12 MHz
+ This might be too fast for stable SDIO operations
+
+ MKS Robin SDIO seems stable with BusWide 1bit and ClockDiv 8 (i.e., 4.8MHz SDIO clock frequency)
+ More testing is required as there are clearly some 4bit init problems.
+ */
+
+ void go_to_transfer_speed() {
+ /* Default SDIO peripheral configuration for SD card initialization */
+ hsd.Init.ClockEdge = hsd.Init.ClockEdge;
+ hsd.Init.ClockBypass = hsd.Init.ClockBypass;
+ hsd.Init.ClockPowerSave = hsd.Init.ClockPowerSave;
+ hsd.Init.BusWide = hsd.Init.BusWide;
+ hsd.Init.HardwareFlowControl = hsd.Init.HardwareFlowControl;
+ hsd.Init.ClockDiv = clock_to_divider(SDIO_CLOCK);
+
+ /* Initialize SDIO peripheral interface with default configuration */
+ SDIO_Init(hsd.Instance, hsd.Init);
+ }
+
+ void SD_LowLevel_Init() {
+ uint32_t tempreg;
+
+ // Enable GPIO clocks
+ __HAL_RCC_GPIOC_CLK_ENABLE();
+ __HAL_RCC_GPIOD_CLK_ENABLE();
+
+ GPIO_InitTypeDef GPIO_InitStruct;
+
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = 1; // GPIO_NOPULL
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
+
+ #if DISABLED(STM32F1xx)
+ GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
+ #endif
+
+ GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_12; // D0 & SCK
+ HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
+
+ #if PINS_EXIST(SDIO_D1, SDIO_D2, SDIO_D3) // define D1-D3 only if have a four bit wide SDIO bus
+ GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11; // D1-D3
+ HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
+ #endif
+
+ // Configure PD.02 CMD line
+ GPIO_InitStruct.Pin = GPIO_PIN_2;
+ HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
+
+ // Setup DMA
+ #ifdef STM32F1xx
+ hdma_sdio.Init.Mode = DMA_NORMAL;
+ hdma_sdio.Instance = DMA2_Channel4;
+ HAL_NVIC_EnableIRQ(DMA2_Channel4_5_IRQn);
+ #elif defined(STM32F4xx)
+ hdma_sdio.Init.Mode = DMA_PFCTRL;
+ hdma_sdio.Instance = DMA2_Stream3;
+ hdma_sdio.Init.Channel = DMA_CHANNEL_4;
+ hdma_sdio.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
+ hdma_sdio.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
+ hdma_sdio.Init.MemBurst = DMA_MBURST_INC4;
+ hdma_sdio.Init.PeriphBurst = DMA_PBURST_INC4;
+ HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
+ #endif
+ HAL_NVIC_EnableIRQ(SDIO_IRQn);
+ hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE;
+ hdma_sdio.Init.MemInc = DMA_MINC_ENABLE;
+ hdma_sdio.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
+ hdma_sdio.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
+ hdma_sdio.Init.Priority = DMA_PRIORITY_LOW;
+ __HAL_LINKDMA(&hsd, hdmarx, hdma_sdio);
+ __HAL_LINKDMA(&hsd, hdmatx, hdma_sdio);
+
+ #ifdef STM32F1xx
+ __HAL_RCC_SDIO_CLK_ENABLE();
+ __HAL_RCC_DMA2_CLK_ENABLE();
+ #else
+ __HAL_RCC_SDIO_FORCE_RESET(); delay(2);
+ __HAL_RCC_SDIO_RELEASE_RESET(); delay(2);
+ __HAL_RCC_SDIO_CLK_ENABLE();
+
+ __HAL_RCC_DMA2_FORCE_RESET(); delay(2);
+ __HAL_RCC_DMA2_RELEASE_RESET(); delay(2);
+ __HAL_RCC_DMA2_CLK_ENABLE();
+ #endif
+
+ // Initialize the SDIO (with initial <400Khz Clock)
+ tempreg = 0 // Reset value
+ | SDIO_CLKCR_CLKEN // Clock enabled
+ | SDIO_INIT_CLK_DIV; // Clock Divider. Clock = 48000 / (118 + 2) = 400Khz
+ // Keep the rest at 0 => HW_Flow Disabled, Rising Clock Edge, Disable CLK ByPass, Bus Width = 0, Power save Disable
+ SDIO->CLKCR = tempreg;
+
+ // Power up the SDIO
+ SDIO_PowerState_ON(SDIO);
+ hsd.Instance = SDIO;
+ }
+
+ bool SDIO_Init() {
+ uint8_t retryCnt = SDIO_READ_RETRIES;
+
+ bool status;
+ hsd.Instance = SDIO;
+ hsd.State = HAL_SD_STATE_RESET;
+
+ SD_LowLevel_Init();
+
+ uint8_t retry_Cnt = retryCnt;
+ for (;;) {
+ hal.watchdog_refresh();
+ status = (bool) HAL_SD_Init(&hsd);
+ if (!status) break;
+ if (!--retry_Cnt) return false; // return failing status if retries are exhausted
+ }
+
+ go_to_transfer_speed();
+
+ hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_ENABLE;
+ hsd.Init.ClockDiv = 8;
+
+ #if PINS_EXIST(SDIO_D1, SDIO_D2, SDIO_D3) // go to 4 bit wide mode if pins are defined
+ retry_Cnt = retryCnt;
+ for (;;) {
+ hal.watchdog_refresh();
+ if (!HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B)) break; // some cards are only 1 bit wide so a pass here is not required
+ if (!--retry_Cnt) break;
+ }
+ if (!retry_Cnt) { // wide bus failed, go back to one bit wide mode
+ hsd.State = (HAL_SD_StateTypeDef) 0; // HAL_SD_STATE_RESET
+ SD_LowLevel_Init();
+ retry_Cnt = retryCnt;
+ for (;;) {
+ hal.watchdog_refresh();
+ status = (bool) HAL_SD_Init(&hsd);
+ if (!status) break;
+ if (!--retry_Cnt) return false; // return failing status if retries are exhausted
+ }
+ go_to_transfer_speed();
+ }
+ #endif
+
+ return true;
+ }
+
+ /**
+ * @brief Read or Write a block
+ * @details Read or Write a block with SDIO
+ *
+ * @param block The block index
+ * @param src The data buffer source for a write
+ * @param dst The data buffer destination for a read
+ *
+ * @return true on success
+ */
+ static bool SDIO_ReadWriteBlock_DMA(uint32_t block, const uint8_t *src, uint8_t *dst) {
+ if (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) return false;
+
+ hal.watchdog_refresh();
+
+ HAL_StatusTypeDef ret;
+ if (src) {
+ hdma_sdio.Init.Direction = DMA_MEMORY_TO_PERIPH;
+ HAL_DMA_Init(&hdma_sdio);
+ ret = HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t*)src, block, 1);
+ }
+ else {
+ hdma_sdio.Init.Direction = DMA_PERIPH_TO_MEMORY;
+ HAL_DMA_Init(&hdma_sdio);
+ ret = HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t*)dst, block, 1);
+ }
+
+ if (ret != HAL_OK) {
+ HAL_DMA_Abort_IT(&hdma_sdio);
+ HAL_DMA_DeInit(&hdma_sdio);
+ return false;
+ }
+
+ millis_t timeout = millis() + SD_TIMEOUT;
+ // Wait the transfer
+ while (hsd.State != HAL_SD_STATE_READY) {
+ if (ELAPSED(millis(), timeout)) {
+ HAL_DMA_Abort_IT(&hdma_sdio);
+ HAL_DMA_DeInit(&hdma_sdio);
+ return false;
+ }
+ }
+
+ while (__HAL_DMA_GET_FLAG(&hdma_sdio, __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_sdio)) != 0
+ || __HAL_DMA_GET_FLAG(&hdma_sdio, __HAL_DMA_GET_TE_FLAG_INDEX(&hdma_sdio)) != 0) { /* nada */ }
+
+ HAL_DMA_Abort_IT(&hdma_sdio);
+ HAL_DMA_DeInit(&hdma_sdio);
+
+ timeout = millis() + SD_TIMEOUT;
+ while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) if (ELAPSED(millis(), timeout)) return false;
+
+ return true;
+ }
+
+#endif // !SDIO_FOR_STM32H7
+
+/**
+ * @brief Read a block
+ * @details Read a block from media with SDIO
+ *
+ * @param block The block index
+ * @param src The block buffer
+ *
+ * @return true on success
+ */
+bool SDIO_ReadBlock(uint32_t block, uint8_t *dst) {
+ #ifdef SDIO_FOR_STM32H7
+
+ uint32_t timeout = HAL_GetTick() + SD_TIMEOUT;
+
+ while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
+ if (HAL_GetTick() >= timeout) return false;
+
+ waitingRxCplt = 1;
+ if (HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t*)dst, block, 1) != HAL_OK)
+ return false;
+
+ timeout = HAL_GetTick() + SD_TIMEOUT;
+ while (waitingRxCplt)
+ if (HAL_GetTick() >= timeout) return false;
+
+ return true;
+
+ #else
+
+ uint8_t retries = SDIO_READ_RETRIES;
+ while (retries--) if (SDIO_ReadWriteBlock_DMA(block, nullptr, dst)) return true;
+ return false;
+
+ #endif
+}
+
+/**
+ * @brief Write a block
+ * @details Write a block to media with SDIO
+ *
+ * @param block The block index
+ * @param src The block data
+ *
+ * @return true on success
+ */
+bool SDIO_WriteBlock(uint32_t block, const uint8_t *src) {
+ #ifdef SDIO_FOR_STM32H7
+
+ uint32_t timeout = HAL_GetTick() + SD_TIMEOUT;
+
+ while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
+ if (HAL_GetTick() >= timeout) return false;
+
+ waitingTxCplt = 1;
+ if (HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t*)src, block, 1) != HAL_OK)
+ return false;
+
+ timeout = HAL_GetTick() + SD_TIMEOUT;
+ while (waitingTxCplt)
+ if (HAL_GetTick() >= timeout) return false;
+
+ return true;
+
+ #else
+
+ uint8_t retries = SDIO_READ_RETRIES;
+ while (retries--) {
+ if (SDIO_ReadWriteBlock_DMA(block, src, nullptr)) return true;
+ delay(10);
+ }
+ return false;
+
+ #endif
+}
+
+bool SDIO_IsReady() {
+ return hsd.State == HAL_SD_STATE_READY;
+}
+
+uint32_t SDIO_GetCardSize() {
+ return (uint32_t)(hsd.SdCard.BlockNbr) * (hsd.SdCard.BlockSize);
+}
+
+#endif // SDIO_SUPPORT
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/sdio.h b/Marlin/src/HAL/STM32/sdio.h
new file mode 100644
index 0000000000..cf5539c3c7
--- /dev/null
+++ b/Marlin/src/HAL/STM32/sdio.h
@@ -0,0 +1,29 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 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
+
+#define SDIO_D0_PIN PC8
+#define SDIO_D1_PIN PC9
+#define SDIO_D2_PIN PC10
+#define SDIO_D3_PIN PC11
+#define SDIO_CK_PIN PC12
+#define SDIO_CMD_PIN PD2
diff --git a/Marlin/src/HAL/STM32/spi_pins.h b/Marlin/src/HAL/STM32/spi_pins.h
new file mode 100644
index 0000000000..7f341a8c25
--- /dev/null
+++ b/Marlin/src/HAL/STM32/spi_pins.h
@@ -0,0 +1,38 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+/**
+ * Define SPI Pins: SCK, MISO, MOSI, SS
+ */
+#ifndef SD_SCK_PIN
+ #define SD_SCK_PIN PIN_SPI_SCK
+#endif
+#ifndef SD_MISO_PIN
+ #define SD_MISO_PIN PIN_SPI_MISO
+#endif
+#ifndef SD_MOSI_PIN
+ #define SD_MOSI_PIN PIN_SPI_MOSI
+#endif
+#ifndef SD_SS_PIN
+ #define SD_SS_PIN PIN_SPI_SS
+#endif
diff --git a/Marlin/src/HAL/STM32/tft/gt911.cpp b/Marlin/src/HAL/STM32/tft/gt911.cpp
new file mode 100644
index 0000000000..82b7c5b103
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/gt911.cpp
@@ -0,0 +1,208 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 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 "../../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(TFT_TOUCH_DEVICE_GT911)
+
+#include "gt911.h"
+#include "pinconfig.h"
+
+SW_IIC::SW_IIC(uint16_t sda, uint16_t scl) {
+ scl_pin = scl;
+ sda_pin = sda;
+}
+
+// Software I2C hardware io init
+void SW_IIC::init() {
+ OUT_WRITE(scl_pin, HIGH);
+ OUT_WRITE(sda_pin, HIGH);
+}
+
+// Software I2C start signal
+void SW_IIC::start() {
+ write_sda(HIGH); // SDA = 1
+ write_scl(HIGH); // SCL = 1
+ iic_delay(2);
+ write_sda(LOW); // SDA = 0
+ iic_delay(1);
+ write_scl(LOW); // SCL = 0 // keep SCL low, avoid false stop caused by level jump caused by SDA switching IN/OUT
+}
+
+// Software I2C stop signal
+void SW_IIC::stop() {
+ write_scl(LOW); // SCL = 0
+ iic_delay(2);
+ write_sda(LOW); // SDA = 0
+ iic_delay(2);
+ write_scl(HIGH); // SCL = 1
+ iic_delay(2);
+ write_sda(HIGH); // SDA = 1
+}
+
+// Software I2C sends ACK or NACK signal
+void SW_IIC::send_ack(bool ack) {
+ write_sda(ack ? LOW : HIGH); // SDA = !ack
+ iic_delay(2);
+ write_scl(HIGH); // SCL = 1
+ iic_delay(2);
+ write_scl(LOW); // SCL = 0
+}
+
+// Software I2C read ACK or NACK signal
+bool SW_IIC::read_ack() {
+ bool error = 0;
+ set_sda_in();
+
+ iic_delay(2);
+
+ write_scl(HIGH); // SCL = 1
+ error = read_sda();
+
+ iic_delay(2);
+
+ write_scl(LOW); // SCL = 0
+
+ set_sda_out();
+ return error;
+}
+
+void SW_IIC::send_byte(uint8_t txd) {
+ LOOP_L_N(i, 8) {
+ write_sda(txd & 0x80); // write data bit
+ txd <<= 1;
+ iic_delay(1);
+ write_scl(HIGH); // SCL = 1
+ iic_delay(2);
+ write_scl(LOW); // SCL = 0
+ iic_delay(1);
+ }
+
+ read_ack(); // wait ack
+}
+
+uint8_t SW_IIC::read_byte(bool ack) {
+ uint8_t data = 0;
+
+ set_sda_in();
+ LOOP_L_N(i, 8) {
+ write_scl(HIGH); // SCL = 1
+ iic_delay(1);
+ data <<= 1;
+ if (read_sda()) data++;
+ write_scl(LOW); // SCL = 0
+ iic_delay(2);
+ }
+ set_sda_out();
+
+ send_ack(ack);
+
+ return data;
+}
+
+GT911_REG_MAP GT911::reg;
+SW_IIC GT911::sw_iic = SW_IIC(GT911_SW_I2C_SDA_PIN, GT911_SW_I2C_SCL_PIN);
+
+void GT911::write_reg(uint16_t reg, uint8_t reg_len, uint8_t* w_data, uint8_t w_len) {
+ sw_iic.start();
+ sw_iic.send_byte(gt911_slave_address); // Set IIC Slave address
+ LOOP_L_N(i, reg_len) { // Set reg address
+ uint8_t r = (reg >> (8 * (reg_len - 1 - i))) & 0xFF;
+ sw_iic.send_byte(r);
+ }
+
+ LOOP_L_N(i, w_len) { // Write data to reg
+ sw_iic.send_byte(w_data[i]);
+ }
+ sw_iic.stop();
+}
+
+void GT911::read_reg(uint16_t reg, uint8_t reg_len, uint8_t* r_data, uint8_t r_len) {
+ sw_iic.start();
+ sw_iic.send_byte(gt911_slave_address); // Set IIC Slave address
+ LOOP_L_N(i, reg_len) { // Set reg address
+ uint8_t r = (reg >> (8 * (reg_len - 1 - i))) & 0xFF;
+ sw_iic.send_byte(r);
+ }
+
+ sw_iic.start();
+ sw_iic.send_byte(gt911_slave_address + 1); // Set read mode
+
+ LOOP_L_N(i, r_len)
+ r_data[i] = sw_iic.read_byte(1); // Read data from reg
+
+ sw_iic.stop();
+}
+
+void GT911::Init() {
+ OUT_WRITE(GT911_RST_PIN, LOW);
+ OUT_WRITE(GT911_INT_PIN, LOW);
+ delay(11);
+ WRITE(GT911_INT_PIN, HIGH);
+ delayMicroseconds(110);
+ WRITE(GT911_RST_PIN, HIGH);
+ delay(6);
+ WRITE(GT911_INT_PIN, LOW);
+ delay(55);
+ SET_INPUT(GT911_INT_PIN);
+
+ sw_iic.init();
+
+ uint8_t clear_reg = 0x00;
+ write_reg(0x814E, 2, &clear_reg, 1); // Reset to 0 for start
+}
+
+bool GT911::getFirstTouchPoint(int16_t *x, int16_t *y) {
+ read_reg(0x814E, 2, ®.REG.status, 1);
+
+ if (reg.REG.status >= 0x80 && reg.REG.status <= 0x85) {
+ read_reg(0x8150, 2, reg.map + 2, 38);
+ uint8_t clear_reg = 0x00;
+ write_reg(0x814E, 2, &clear_reg, 1); // Reset to 0 for start
+ // First touch point
+ *x = ((reg.REG.point[0].xh & 0x0F) << 8) | reg.REG.point[0].xl;
+ *y = ((reg.REG.point[0].yh & 0x0F) << 8) | reg.REG.point[0].yl;
+ return true;
+ }
+ return false;
+}
+
+bool GT911::getPoint(int16_t *x, int16_t *y) {
+ static bool touched = 0;
+ static int16_t read_x = 0, read_y = 0;
+ static millis_t next_time = 0;
+
+ if (ELAPSED(millis(), next_time)) {
+ touched = getFirstTouchPoint(&read_x, &read_y);
+ next_time = millis() + 20;
+ }
+
+ *x = read_x;
+ *y = read_y;
+ return touched;
+}
+
+#endif // TFT_TOUCH_DEVICE_GT911
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/tft/gt911.h b/Marlin/src/HAL/STM32/tft/gt911.h
new file mode 100644
index 0000000000..260c195eca
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/gt911.h
@@ -0,0 +1,96 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 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/MarlinConfig.h"
+
+#define GT911_SLAVE_ADDRESS 0x28
+
+#if !PIN_EXISTS(GT911_RST)
+ #error "GT911_RST_PIN is not defined."
+#elif !PIN_EXISTS(GT911_INT)
+ #error "GT911_INT_PIN is not defined."
+#elif !PIN_EXISTS(GT911_SW_I2C_SCL)
+ #error "GT911_SW_I2C_SCL_PIN is not defined."
+#elif !PIN_EXISTS(GT911_SW_I2C_SDA)
+ #error "GT911_SW_I2C_SDA_PIN is not defined."
+#endif
+
+class SW_IIC {
+ private:
+ uint16_t scl_pin;
+ uint16_t sda_pin;
+ void write_scl(bool level) { WRITE(scl_pin, level); }
+ void write_sda(bool level) { WRITE(sda_pin, level); }
+ bool read_sda() { return READ(sda_pin); }
+ void set_sda_out() { SET_OUTPUT(sda_pin); }
+ void set_sda_in() { SET_INPUT_PULLUP(sda_pin); }
+ static void iic_delay(uint8_t t) { delayMicroseconds(t); }
+
+ public:
+ SW_IIC(uint16_t sda, uint16_t scl);
+ // setSCL/SDA have to be called before begin()
+ void setSCL(uint16_t scl) { scl_pin = scl; }
+ void setSDA(uint16_t sda) { sda_pin = sda; }
+ void init(); // Initialize the IO port of IIC
+ void start(); // Send IIC start signal
+ void stop(); // Send IIC stop signal
+ void send_byte(uint8_t txd); // IIC sends a byte
+ uint8_t read_byte(bool ack); // IIC reads a byte
+ void send_ack(bool ack); // IIC sends ACK or NACK signal
+ bool read_ack();
+};
+
+typedef struct __attribute__((__packed__)) {
+ uint8_t xl;
+ uint8_t xh;
+ uint8_t yl;
+ uint8_t yh;
+ uint8_t sizel;
+ uint8_t sizeh;
+ uint8_t reserved;
+ uint8_t track_id;
+} GT911_POINT;
+
+typedef union __attribute__((__packed__)) {
+ uint8_t map[42];
+ struct {
+ uint8_t status; // 0x814E
+ uint8_t track_id; // 0x814F
+
+ GT911_POINT point[5]; // [0]:0x8150 - 0x8157 / [1]:0x8158 - 0x815F / [2]:0x8160 - 0x8167 / [3]:0x8168 - 0x816F / [4]:0x8170 - 0x8177
+ } REG;
+} GT911_REG_MAP;
+
+class GT911 {
+ private:
+ static const uint8_t gt911_slave_address = GT911_SLAVE_ADDRESS;
+ static GT911_REG_MAP reg;
+ static SW_IIC sw_iic;
+ static void write_reg(uint16_t reg, uint8_t reg_len, uint8_t* w_data, uint8_t w_len);
+ static void read_reg(uint16_t reg, uint8_t reg_len, uint8_t* r_data, uint8_t r_len);
+
+ public:
+ static void Init();
+ static bool getFirstTouchPoint(int16_t *x, int16_t *y);
+ static bool getPoint(int16_t *x, int16_t *y);
+};
diff --git a/Marlin/src/HAL/STM32/tft/tft_fsmc.cpp b/Marlin/src/HAL/STM32/tft/tft_fsmc.cpp
new file mode 100644
index 0000000000..cf9e569336
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/tft_fsmc.cpp
@@ -0,0 +1,189 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../../inc/MarlinConfig.h"
+
+#if HAS_FSMC_TFT
+
+#include "tft_fsmc.h"
+#include "pinconfig.h"
+
+SRAM_HandleTypeDef TFT_FSMC::SRAMx;
+DMA_HandleTypeDef TFT_FSMC::DMAtx;
+LCD_CONTROLLER_TypeDef *TFT_FSMC::LCD;
+
+void TFT_FSMC::Init() {
+ uint32_t controllerAddress;
+ FSMC_NORSRAM_TimingTypeDef Timing, ExtTiming;
+
+ uint32_t NSBank = (uint32_t)pinmap_peripheral(digitalPinToPinName(TFT_CS_PIN), PinMap_FSMC_CS);
+
+ // Perform the SRAM1 memory initialization sequence
+ SRAMx.Instance = FSMC_NORSRAM_DEVICE;
+ SRAMx.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
+ // SRAMx.Init
+ SRAMx.Init.NSBank = NSBank;
+ SRAMx.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
+ SRAMx.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
+ SRAMx.Init.MemoryDataWidth = TERN(TFT_INTERFACE_FSMC_8BIT, FSMC_NORSRAM_MEM_BUS_WIDTH_8, FSMC_NORSRAM_MEM_BUS_WIDTH_16);
+ SRAMx.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
+ SRAMx.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
+ SRAMx.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
+ SRAMx.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
+ SRAMx.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
+ SRAMx.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
+ SRAMx.Init.ExtendedMode = FSMC_EXTENDED_MODE_ENABLE;
+ SRAMx.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
+ SRAMx.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
+ #ifdef STM32F4xx
+ SRAMx.Init.PageSize = FSMC_PAGE_SIZE_NONE;
+ #endif
+ // Read Timing - relatively slow to ensure ID information is correctly read from TFT controller
+ // Can be decreases from 15-15-24 to 4-4-8 with risk of stability loss
+ Timing.AddressSetupTime = 15;
+ Timing.AddressHoldTime = 15;
+ Timing.DataSetupTime = 24;
+ Timing.BusTurnAroundDuration = 0;
+ Timing.CLKDivision = 16;
+ Timing.DataLatency = 17;
+ Timing.AccessMode = FSMC_ACCESS_MODE_A;
+ // Write Timing
+ // Can be decreases from 8-15-8 to 0-0-1 with risk of stability loss
+ ExtTiming.AddressSetupTime = 8;
+ ExtTiming.AddressHoldTime = 15;
+ ExtTiming.DataSetupTime = 8;
+ ExtTiming.BusTurnAroundDuration = 0;
+ ExtTiming.CLKDivision = 16;
+ ExtTiming.DataLatency = 17;
+ ExtTiming.AccessMode = FSMC_ACCESS_MODE_A;
+
+ __HAL_RCC_FSMC_CLK_ENABLE();
+
+ for (uint16_t i = 0; PinMap_FSMC[i].pin != NC; i++)
+ pinmap_pinout(PinMap_FSMC[i].pin, PinMap_FSMC);
+ pinmap_pinout(digitalPinToPinName(TFT_CS_PIN), PinMap_FSMC_CS);
+ pinmap_pinout(digitalPinToPinName(TFT_RS_PIN), PinMap_FSMC_RS);
+
+ controllerAddress = FSMC_BANK1_1;
+ #ifdef PF0
+ switch (NSBank) {
+ case FSMC_NORSRAM_BANK2: controllerAddress = FSMC_BANK1_2 ; break;
+ case FSMC_NORSRAM_BANK3: controllerAddress = FSMC_BANK1_3 ; break;
+ case FSMC_NORSRAM_BANK4: controllerAddress = FSMC_BANK1_4 ; break;
+ }
+ #endif
+
+ controllerAddress |= (uint32_t)pinmap_peripheral(digitalPinToPinName(TFT_RS_PIN), PinMap_FSMC_RS);
+
+ HAL_SRAM_Init(&SRAMx, &Timing, &ExtTiming);
+
+ __HAL_RCC_DMA2_CLK_ENABLE();
+
+ #ifdef STM32F1xx
+ DMAtx.Instance = DMA2_Channel1;
+ #elif defined(STM32F4xx)
+ DMAtx.Instance = DMA2_Stream0;
+ DMAtx.Init.Channel = DMA_CHANNEL_0;
+ DMAtx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
+ DMAtx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
+ DMAtx.Init.MemBurst = DMA_MBURST_SINGLE;
+ DMAtx.Init.PeriphBurst = DMA_PBURST_SINGLE;
+ #endif
+
+ DMAtx.Init.Direction = DMA_MEMORY_TO_MEMORY;
+ DMAtx.Init.MemInc = DMA_MINC_DISABLE;
+ DMAtx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
+ DMAtx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
+ DMAtx.Init.Mode = DMA_NORMAL;
+ DMAtx.Init.Priority = DMA_PRIORITY_HIGH;
+
+ LCD = (LCD_CONTROLLER_TypeDef *)controllerAddress;
+}
+
+uint32_t TFT_FSMC::GetID() {
+ uint32_t id;
+ WriteReg(0);
+ id = LCD->RAM;
+
+ if (id == 0)
+ id = ReadID(LCD_READ_ID);
+ if ((id & 0xFFFF) == 0 || (id & 0xFFFF) == 0xFFFF)
+ id = ReadID(LCD_READ_ID4);
+ return id;
+}
+
+uint32_t TFT_FSMC::ReadID(tft_data_t Reg) {
+ uint32_t id;
+ WriteReg(Reg);
+ id = LCD->RAM; // dummy read
+ id = Reg << 24;
+ id |= (LCD->RAM & 0x00FF) << 16;
+ id |= (LCD->RAM & 0x00FF) << 8;
+ id |= LCD->RAM & 0x00FF;
+ return id;
+}
+
+bool TFT_FSMC::isBusy() {
+ #ifdef STM32F1xx
+ #define __IS_DMA_ENABLED(__HANDLE__) ((__HANDLE__)->Instance->CCR & DMA_CCR_EN)
+ #define __IS_DMA_CONFIGURED(__HANDLE__) ((__HANDLE__)->Instance->CPAR != 0)
+ #elif defined(STM32F4xx)
+ #define __IS_DMA_ENABLED(__HANDLE__) ((__HANDLE__)->Instance->CR & DMA_SxCR_EN)
+ #define __IS_DMA_CONFIGURED(__HANDLE__) ((__HANDLE__)->Instance->PAR != 0)
+ #endif
+
+ if (!__IS_DMA_CONFIGURED(&DMAtx)) return false;
+
+ // Check if DMA transfer error or transfer complete flags are set
+ if ((__HAL_DMA_GET_FLAG(&DMAtx, __HAL_DMA_GET_TE_FLAG_INDEX(&DMAtx)) == 0) && (__HAL_DMA_GET_FLAG(&DMAtx, __HAL_DMA_GET_TC_FLAG_INDEX(&DMAtx)) == 0)) return true;
+
+ __DSB();
+ Abort();
+ return false;
+}
+
+void TFT_FSMC::Abort() {
+ HAL_DMA_Abort(&DMAtx); // Abort DMA transfer if any
+ HAL_DMA_DeInit(&DMAtx); // Deconfigure DMA
+}
+
+void TFT_FSMC::TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) {
+ DMAtx.Init.PeriphInc = MemoryIncrease;
+ HAL_DMA_Init(&DMAtx);
+ HAL_DMA_Start(&DMAtx, (uint32_t)Data, (uint32_t)&(LCD->RAM), Count);
+}
+
+void TFT_FSMC::Transmit(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) {
+ DMAtx.Init.PeriphInc = MemoryIncrease;
+ HAL_DMA_Init(&DMAtx);
+ DataTransferBegin();
+ HAL_DMA_Start(&DMAtx, (uint32_t)Data, (uint32_t)&(LCD->RAM), Count);
+ HAL_DMA_PollForTransfer(&DMAtx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
+ Abort();
+}
+
+#endif // HAS_FSMC_TFT
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/tft/tft_fsmc.h b/Marlin/src/HAL/STM32/tft/tft_fsmc.h
new file mode 100644
index 0000000000..41ff8c9a83
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/tft_fsmc.h
@@ -0,0 +1,174 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include "../../../inc/MarlinConfig.h"
+
+#ifdef STM32F1xx
+ #include "stm32f1xx_hal.h"
+#elif defined(STM32F4xx)
+ #include "stm32f4xx_hal.h"
+#else
+ #error "FSMC TFT is currently only supported on STM32F1 and STM32F4 hardware."
+#endif
+
+#ifndef LCD_READ_ID
+ #define LCD_READ_ID 0x04 // Read display identification information (0xD3 on ILI9341)
+#endif
+#ifndef LCD_READ_ID4
+ #define LCD_READ_ID4 0xD3 // Read display identification information (0xD3 on ILI9341)
+#endif
+
+#define DATASIZE_8BIT SPI_DATASIZE_8BIT
+#define DATASIZE_16BIT SPI_DATASIZE_16BIT
+#define TFT_IO_DRIVER TFT_FSMC
+#define DMA_MAX_SIZE 0xFFFF
+
+#define TFT_DATASIZE TERN(TFT_INTERFACE_FSMC_8BIT, DATASIZE_8BIT, DATASIZE_16BIT)
+typedef TERN(TFT_INTERFACE_FSMC_8BIT, uint8_t, uint16_t) tft_data_t;
+
+typedef struct {
+ __IO tft_data_t REG;
+ __IO tft_data_t RAM;
+} LCD_CONTROLLER_TypeDef;
+
+class TFT_FSMC {
+ private:
+ static SRAM_HandleTypeDef SRAMx;
+ static DMA_HandleTypeDef DMAtx;
+
+ static LCD_CONTROLLER_TypeDef *LCD;
+
+ static uint32_t ReadID(tft_data_t Reg);
+ static void Transmit(tft_data_t Data) { LCD->RAM = Data; __DSB(); }
+ static void Transmit(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count);
+ static void TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count);
+
+ public:
+ static void Init();
+ static uint32_t GetID();
+ static bool isBusy();
+ static void Abort();
+
+ static void DataTransferBegin(uint16_t DataWidth = TFT_DATASIZE) {}
+ static void DataTransferEnd() {};
+
+ static void WriteData(uint16_t Data) { Transmit(tft_data_t(Data)); }
+ static void WriteReg(uint16_t Reg) { LCD->REG = tft_data_t(Reg); __DSB(); }
+
+ static void WriteSequence_DMA(uint16_t *Data, uint16_t Count) { TransmitDMA(DMA_PINC_ENABLE, Data, Count); }
+ static void WriteMultiple_DMA(uint16_t Color, uint16_t Count) { static uint16_t Data; Data = Color; TransmitDMA(DMA_PINC_DISABLE, &Data, Count); }
+
+ static void WriteSequence(uint16_t *Data, uint16_t Count) { Transmit(DMA_PINC_ENABLE, Data, Count); }
+ static void WriteMultiple(uint16_t Color, uint32_t Count) {
+ while (Count > 0) {
+ Transmit(DMA_MINC_DISABLE, &Color, Count > DMA_MAX_SIZE ? DMA_MAX_SIZE : Count);
+ Count = Count > DMA_MAX_SIZE ? Count - DMA_MAX_SIZE : 0;
+ }
+ }
+};
+
+#ifdef STM32F1xx
+ #define FSMC_PIN_DATA STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, AFIO_NONE)
+#elif defined(STM32F4xx)
+ #define FSMC_PIN_DATA STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF12_FSMC)
+ #define FSMC_BANK1_1 0x60000000U
+ #define FSMC_BANK1_2 0x64000000U
+ #define FSMC_BANK1_3 0x68000000U
+ #define FSMC_BANK1_4 0x6C000000U
+#else
+ #error No configuration for this MCU
+#endif
+
+const PinMap PinMap_FSMC[] = {
+ {PD_14, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D00
+ {PD_15, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D01
+ {PD_0, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D02
+ {PD_1, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D03
+ {PE_7, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D04
+ {PE_8, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D05
+ {PE_9, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D06
+ {PE_10, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D07
+ #if DISABLED(TFT_INTERFACE_FSMC_8BIT)
+ {PE_11, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D08
+ {PE_12, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D09
+ {PE_13, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D10
+ {PE_14, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D11
+ {PE_15, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D12
+ {PD_8, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D13
+ {PD_9, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D14
+ {PD_10, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D15
+ #endif
+ {PD_4, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_NOE
+ {PD_5, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_NWE
+ {NC, NP, 0}
+};
+
+const PinMap PinMap_FSMC_CS[] = {
+ {PD_7, (void *)FSMC_NORSRAM_BANK1, FSMC_PIN_DATA}, // FSMC_NE1
+ #ifdef PF0
+ {PG_9, (void *)FSMC_NORSRAM_BANK2, FSMC_PIN_DATA}, // FSMC_NE2
+ {PG_10, (void *)FSMC_NORSRAM_BANK3, FSMC_PIN_DATA}, // FSMC_NE3
+ {PG_12, (void *)FSMC_NORSRAM_BANK4, FSMC_PIN_DATA}, // FSMC_NE4
+ #endif
+ {NC, NP, 0}
+};
+
+#if ENABLED(TFT_INTERFACE_FSMC_8BIT)
+ #define FSMC_RS(A) (void *)((2 << (A-1)) - 1)
+#else
+ #define FSMC_RS(A) (void *)((2 << A) - 2)
+#endif
+
+const PinMap PinMap_FSMC_RS[] = {
+ #ifdef PF0
+ {PF_0, FSMC_RS( 0), FSMC_PIN_DATA}, // FSMC_A0
+ {PF_1, FSMC_RS( 1), FSMC_PIN_DATA}, // FSMC_A1
+ {PF_2, FSMC_RS( 2), FSMC_PIN_DATA}, // FSMC_A2
+ {PF_3, FSMC_RS( 3), FSMC_PIN_DATA}, // FSMC_A3
+ {PF_4, FSMC_RS( 4), FSMC_PIN_DATA}, // FSMC_A4
+ {PF_5, FSMC_RS( 5), FSMC_PIN_DATA}, // FSMC_A5
+ {PF_12, FSMC_RS( 6), FSMC_PIN_DATA}, // FSMC_A6
+ {PF_13, FSMC_RS( 7), FSMC_PIN_DATA}, // FSMC_A7
+ {PF_14, FSMC_RS( 8), FSMC_PIN_DATA}, // FSMC_A8
+ {PF_15, FSMC_RS( 9), FSMC_PIN_DATA}, // FSMC_A9
+ {PG_0, FSMC_RS(10), FSMC_PIN_DATA}, // FSMC_A10
+ {PG_1, FSMC_RS(11), FSMC_PIN_DATA}, // FSMC_A11
+ {PG_2, FSMC_RS(12), FSMC_PIN_DATA}, // FSMC_A12
+ {PG_3, FSMC_RS(13), FSMC_PIN_DATA}, // FSMC_A13
+ {PG_4, FSMC_RS(14), FSMC_PIN_DATA}, // FSMC_A14
+ {PG_5, FSMC_RS(15), FSMC_PIN_DATA}, // FSMC_A15
+ #endif
+ {PD_11, FSMC_RS(16), FSMC_PIN_DATA}, // FSMC_A16
+ {PD_12, FSMC_RS(17), FSMC_PIN_DATA}, // FSMC_A17
+ {PD_13, FSMC_RS(18), FSMC_PIN_DATA}, // FSMC_A18
+ {PE_3, FSMC_RS(19), FSMC_PIN_DATA}, // FSMC_A19
+ {PE_4, FSMC_RS(20), FSMC_PIN_DATA}, // FSMC_A20
+ {PE_5, FSMC_RS(21), FSMC_PIN_DATA}, // FSMC_A21
+ {PE_6, FSMC_RS(22), FSMC_PIN_DATA}, // FSMC_A22
+ {PE_2, FSMC_RS(23), FSMC_PIN_DATA}, // FSMC_A23
+ #ifdef PF0
+ {PG_13, FSMC_RS(24), FSMC_PIN_DATA}, // FSMC_A24
+ {PG_14, FSMC_RS(25), FSMC_PIN_DATA}, // FSMC_A25
+ #endif
+ {NC, NP, 0}
+};
diff --git a/Marlin/src/HAL/STM32/tft/tft_ltdc.cpp b/Marlin/src/HAL/STM32/tft/tft_ltdc.cpp
new file mode 100644
index 0000000000..2be900618f
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/tft_ltdc.cpp
@@ -0,0 +1,389 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 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 "../../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../../inc/MarlinConfig.h"
+
+#if HAS_LTDC_TFT
+
+#include "tft_ltdc.h"
+#include "pinconfig.h"
+
+#define FRAME_BUFFER_ADDRESS 0XC0000000 // SDRAM address
+
+#define SDRAM_TIMEOUT ((uint32_t)0xFFFF)
+#define REFRESH_COUNT ((uint32_t)0x02A5) // SDRAM refresh counter
+
+#define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000)
+#define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001)
+#define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002)
+#define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004)
+#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)
+#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008)
+#define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020)
+#define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030)
+#define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000)
+#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
+#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)
+
+void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_CommandTypeDef *Command) {
+
+ __IO uint32_t tmpmrd =0;
+ /* Step 1: Configure a clock configuration enable command */
+ Command->CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
+ Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
+ Command->AutoRefreshNumber = 1;
+ Command->ModeRegisterDefinition = 0;
+ /* Send the command */
+ HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
+
+ /* Step 2: Insert 100 us minimum delay */
+ /* Inserted delay is equal to 1 ms due to systick time base unit (ms) */
+ HAL_Delay(1);
+
+ /* Step 3: Configure a PALL (precharge all) command */
+ Command->CommandMode = FMC_SDRAM_CMD_PALL;
+ Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
+ Command->AutoRefreshNumber = 1;
+ Command->ModeRegisterDefinition = 0;
+ /* Send the command */
+ HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
+
+ /* Step 4 : Configure a Auto-Refresh command */
+ Command->CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
+ Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
+ Command->AutoRefreshNumber = 8;
+ Command->ModeRegisterDefinition = 0;
+ /* Send the command */
+ HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
+
+ /* Step 5: Program the external memory mode register */
+ tmpmrd = (uint32_t)(SDRAM_MODEREG_BURST_LENGTH_1 |
+ SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |
+ SDRAM_MODEREG_CAS_LATENCY_2 |
+ SDRAM_MODEREG_OPERATING_MODE_STANDARD |
+ SDRAM_MODEREG_WRITEBURST_MODE_SINGLE);
+
+ Command->CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
+ Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
+ Command->AutoRefreshNumber = 1;
+ Command->ModeRegisterDefinition = tmpmrd;
+ /* Send the command */
+ HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
+
+ /* Step 6: Set the refresh rate counter */
+ /* Set the device refresh rate */
+ HAL_SDRAM_ProgramRefreshRate(hsdram, REFRESH_COUNT);
+}
+
+void SDRAM_Config() {
+
+ __HAL_RCC_SYSCFG_CLK_ENABLE();
+ __HAL_RCC_FMC_CLK_ENABLE();
+
+ SDRAM_HandleTypeDef hsdram;
+ FMC_SDRAM_TimingTypeDef SDRAM_Timing;
+ FMC_SDRAM_CommandTypeDef command;
+
+ /* Configure the SDRAM device */
+ hsdram.Instance = FMC_SDRAM_DEVICE;
+ hsdram.Init.SDBank = FMC_SDRAM_BANK1;
+ hsdram.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
+ hsdram.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
+ hsdram.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
+ hsdram.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
+ hsdram.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_2;
+ hsdram.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
+ hsdram.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
+ hsdram.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
+ hsdram.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
+
+ /* Timing configuration for 100Mhz as SDRAM clock frequency (System clock is up to 200Mhz) */
+ SDRAM_Timing.LoadToActiveDelay = 2;
+ SDRAM_Timing.ExitSelfRefreshDelay = 8;
+ SDRAM_Timing.SelfRefreshTime = 6;
+ SDRAM_Timing.RowCycleDelay = 6;
+ SDRAM_Timing.WriteRecoveryTime = 2;
+ SDRAM_Timing.RPDelay = 2;
+ SDRAM_Timing.RCDDelay = 2;
+
+ /* Initialize the SDRAM controller */
+ if (HAL_SDRAM_Init(&hsdram, &SDRAM_Timing) != HAL_OK)
+ {
+ /* Initialization Error */
+ }
+
+ /* Program the SDRAM external device */
+ SDRAM_Initialization_Sequence(&hsdram, &command);
+}
+
+void LTDC_Config() {
+
+ __HAL_RCC_LTDC_CLK_ENABLE();
+ __HAL_RCC_DMA2D_CLK_ENABLE();
+
+ RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
+
+ /* The PLL3R is configured to provide the LTDC PCLK clock */
+ /* PLL3_VCO Input = HSE_VALUE / PLL3M = 25Mhz / 5 = 5 Mhz */
+ /* PLL3_VCO Output = PLL3_VCO Input * PLL3N = 5Mhz * 160 = 800 Mhz */
+ /* PLLLCDCLK = PLL3_VCO Output/PLL3R = 800Mhz / 16 = 50Mhz */
+ /* LTDC clock frequency = PLLLCDCLK = 50 Mhz */
+ PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
+ PeriphClkInitStruct.PLL3.PLL3M = 5;
+ PeriphClkInitStruct.PLL3.PLL3N = 160;
+ PeriphClkInitStruct.PLL3.PLL3FRACN = 0;
+ PeriphClkInitStruct.PLL3.PLL3P = 2;
+ PeriphClkInitStruct.PLL3.PLL3Q = 2;
+ PeriphClkInitStruct.PLL3.PLL3R = (800 / LTDC_LCD_CLK);
+ PeriphClkInitStruct.PLL3.PLL3VCOSEL = RCC_PLL3VCOWIDE;
+ PeriphClkInitStruct.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_2;
+ HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
+
+ LTDC_HandleTypeDef hltdc_F;
+ LTDC_LayerCfgTypeDef pLayerCfg;
+
+ /* LTDC Initialization -------------------------------------------------------*/
+
+ /* Polarity configuration */
+ /* Initialize the horizontal synchronization polarity as active low */
+ hltdc_F.Init.HSPolarity = LTDC_HSPOLARITY_AL;
+ /* Initialize the vertical synchronization polarity as active low */
+ hltdc_F.Init.VSPolarity = LTDC_VSPOLARITY_AL;
+ /* Initialize the data enable polarity as active low */
+ hltdc_F.Init.DEPolarity = LTDC_DEPOLARITY_AL;
+ /* Initialize the pixel clock polarity as input pixel clock */
+ hltdc_F.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
+
+ /* Timing configuration */
+ hltdc_F.Init.HorizontalSync = (LTDC_LCD_HSYNC - 1);
+ hltdc_F.Init.VerticalSync = (LTDC_LCD_VSYNC - 1);
+ hltdc_F.Init.AccumulatedHBP = (LTDC_LCD_HSYNC + LTDC_LCD_HBP - 1);
+ hltdc_F.Init.AccumulatedVBP = (LTDC_LCD_VSYNC + LTDC_LCD_VBP - 1);
+ hltdc_F.Init.AccumulatedActiveH = (TFT_HEIGHT + LTDC_LCD_VSYNC + LTDC_LCD_VBP - 1);
+ hltdc_F.Init.AccumulatedActiveW = (TFT_WIDTH + LTDC_LCD_HSYNC + LTDC_LCD_HBP - 1);
+ hltdc_F.Init.TotalHeigh = (TFT_HEIGHT + LTDC_LCD_VSYNC + LTDC_LCD_VBP + LTDC_LCD_VFP - 1);
+ hltdc_F.Init.TotalWidth = (TFT_WIDTH + LTDC_LCD_HSYNC + LTDC_LCD_HBP + LTDC_LCD_HFP - 1);
+
+ /* Configure R,G,B component values for LCD background color : all black background */
+ hltdc_F.Init.Backcolor.Blue = 0;
+ hltdc_F.Init.Backcolor.Green = 0;
+ hltdc_F.Init.Backcolor.Red = 0;
+
+ hltdc_F.Instance = LTDC;
+
+ /* Layer0 Configuration ------------------------------------------------------*/
+
+ /* Windowing configuration */
+ pLayerCfg.WindowX0 = 0;
+ pLayerCfg.WindowX1 = TFT_WIDTH;
+ pLayerCfg.WindowY0 = 0;
+ pLayerCfg.WindowY1 = TFT_HEIGHT;
+
+ /* Pixel Format configuration*/
+ pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
+
+ /* Start Address configuration : frame buffer is located at SDRAM memory */
+ pLayerCfg.FBStartAdress = (uint32_t)(FRAME_BUFFER_ADDRESS);
+
+ /* Alpha constant (255 == totally opaque) */
+ pLayerCfg.Alpha = 255;
+
+ /* Default Color configuration (configure A,R,G,B component values) : no background color */
+ pLayerCfg.Alpha0 = 0; /* fully transparent */
+ pLayerCfg.Backcolor.Blue = 0;
+ pLayerCfg.Backcolor.Green = 0;
+ pLayerCfg.Backcolor.Red = 0;
+
+ /* Configure blending factors */
+ pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
+ pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
+
+ /* Configure the number of lines and number of pixels per line */
+ pLayerCfg.ImageWidth = TFT_WIDTH;
+ pLayerCfg.ImageHeight = TFT_HEIGHT;
+
+ /* Configure the LTDC */
+ if (HAL_LTDC_Init(&hltdc_F) != HAL_OK)
+ {
+ /* Initialization Error */
+ }
+
+ /* Configure the Layer*/
+ if (HAL_LTDC_ConfigLayer(&hltdc_F, &pLayerCfg, 0) != HAL_OK)
+ {
+ /* Initialization Error */
+ }
+}
+
+uint16_t TFT_LTDC::x_min = 0;
+uint16_t TFT_LTDC::x_max = 0;
+uint16_t TFT_LTDC::y_min = 0;
+uint16_t TFT_LTDC::y_max = 0;
+uint16_t TFT_LTDC::x_cur = 0;
+uint16_t TFT_LTDC::y_cur = 0;
+uint8_t TFT_LTDC::reg = 0;
+volatile uint16_t* TFT_LTDC::framebuffer = (volatile uint16_t* )FRAME_BUFFER_ADDRESS;
+
+void TFT_LTDC::Init() {
+
+ // SDRAM pins init
+ for (uint16_t i = 0; PinMap_SDRAM[i].pin != NC; i++)
+ pinmap_pinout(PinMap_SDRAM[i].pin, PinMap_SDRAM);
+
+ // SDRAM peripheral config
+ SDRAM_Config();
+
+ // LTDC pins init
+ for (uint16_t i = 0; PinMap_LTDC[i].pin != NC; i++)
+ pinmap_pinout(PinMap_LTDC[i].pin, PinMap_LTDC);
+
+ // LTDC peripheral config
+ LTDC_Config();
+}
+
+uint32_t TFT_LTDC::GetID() {
+ return 0xABAB;
+}
+
+uint32_t TFT_LTDC::ReadID(tft_data_t Reg) {
+ return 0xABAB;
+}
+
+bool TFT_LTDC::isBusy() {
+ return false;
+}
+
+uint16_t TFT_LTDC::ReadPoint(uint16_t x, uint16_t y) {
+ return framebuffer[(TFT_WIDTH * y) + x];
+}
+
+void TFT_LTDC::DrawPoint(uint16_t x, uint16_t y, uint16_t color) {
+ framebuffer[(TFT_WIDTH * y) + x] = color;
+}
+
+void TFT_LTDC::DrawRect(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t color) {
+
+ if (sx == ex || sy == ey) return;
+
+ uint16_t offline = TFT_WIDTH - (ex - sx);
+ uint32_t addr = (uint32_t)&framebuffer[(TFT_WIDTH * sy) + sx];
+
+ CBI(DMA2D->CR, 0);
+ DMA2D->CR = 3 << 16;
+ DMA2D->OPFCCR = 0X02;
+ DMA2D->OOR = offline;
+ DMA2D->OMAR = addr;
+ DMA2D->NLR = (ey - sy) | ((ex - sx) << 16);
+ DMA2D->OCOLR = color;
+ SBI(DMA2D->CR, 0);
+
+ uint32_t timeout = 0;
+ while (!TEST(DMA2D->ISR, 1)) {
+ timeout++;
+ if (timeout > 0x1FFFFF) break;
+ }
+ SBI(DMA2D->IFCR, 1);
+}
+
+void TFT_LTDC::DrawImage(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *colors) {
+
+ if (sx == ex || sy == ey) return;
+
+ uint16_t offline = TFT_WIDTH - (ex - sx);
+ uint32_t addr = (uint32_t)&framebuffer[(TFT_WIDTH * sy) + sx];
+
+ CBI(DMA2D->CR, 0);
+ DMA2D->CR = 0 << 16;
+ DMA2D->FGPFCCR = 0X02;
+ DMA2D->FGOR = 0;
+ DMA2D->OOR = offline;
+ DMA2D->FGMAR = (uint32_t)colors;
+ DMA2D->OMAR = addr;
+ DMA2D->NLR = (ey - sy) | ((ex - sx) << 16);
+ SBI(DMA2D->CR, 0);
+
+ uint32_t timeout = 0;
+ while (!TEST(DMA2D->ISR, 1)) {
+ timeout++;
+ if (timeout > 0x1FFFFF) break;
+ }
+ SBI(DMA2D->IFCR, 1);
+}
+
+void TFT_LTDC::WriteData(uint16_t data) {
+ switch (reg) {
+ case 0x01: x_cur = x_min = data; return;
+ case 0x02: x_max = data; return;
+ case 0x03: y_cur = y_min = data; return;
+ case 0x04: y_max = data; return;
+ }
+ Transmit(data);
+}
+
+void TFT_LTDC::Transmit(tft_data_t Data) {
+ DrawPoint(x_cur, y_cur, Data);
+ x_cur++;
+ if (x_cur > x_max) {
+ x_cur = x_min;
+ y_cur++;
+ if (y_cur > y_max) y_cur = y_min;
+ }
+}
+
+void TFT_LTDC::WriteReg(uint16_t Reg) {
+ reg = Reg;
+}
+
+void TFT_LTDC::Transmit(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) {
+
+ while (x_cur != x_min && Count) {
+ Transmit(*Data);
+ if (MemoryIncrease == DMA_PINC_ENABLE) Data++;
+ Count--;
+ }
+
+ uint16_t width = x_max - x_min + 1;
+ uint16_t height = Count / width;
+ uint16_t x_end_cnt = Count - (width * height);
+
+ if (height) {
+ if (MemoryIncrease == DMA_PINC_ENABLE) {
+ DrawImage(x_min, y_cur, x_min + width, y_cur + height, Data);
+ Data += width * height;
+ }
+ else
+ DrawRect(x_min, y_cur, x_min + width, y_cur + height, *Data);
+ y_cur += height;
+ }
+
+ while (x_end_cnt) {
+ Transmit(*Data);
+ if (MemoryIncrease == DMA_PINC_ENABLE) Data++;
+ x_end_cnt--;
+ }
+}
+
+#endif // HAS_LTDC_TFT
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/tft/tft_ltdc.h b/Marlin/src/HAL/STM32/tft/tft_ltdc.h
new file mode 100644
index 0000000000..8d83839bb3
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/tft_ltdc.h
@@ -0,0 +1,158 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 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/MarlinConfig.h"
+
+#ifdef STM32H7xx
+ #include "stm32h7xx_hal.h"
+#else
+ #error "LTDC TFT is currently only supported on STM32H7 hardware."
+#endif
+
+#define DATASIZE_8BIT SPI_DATASIZE_8BIT
+#define DATASIZE_16BIT SPI_DATASIZE_16BIT
+#define TFT_IO_DRIVER TFT_LTDC
+#define DMA_MAX_SIZE 0xFFFF
+
+#define TFT_DATASIZE DATASIZE_16BIT
+typedef uint16_t tft_data_t;
+
+class TFT_LTDC {
+ private:
+ static volatile uint16_t *framebuffer;
+ static uint16_t x_min, x_max, y_min, y_max, x_cur, y_cur;
+ static uint8_t reg;
+
+ static uint32_t ReadID(tft_data_t Reg);
+
+ static uint16_t ReadPoint(uint16_t x, uint16_t y);
+ static void DrawPoint(uint16_t x, uint16_t y, uint16_t color);
+ static void DrawRect(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t color);
+ static void DrawImage(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *colors);
+ static void Transmit(tft_data_t Data);
+ static void Transmit(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count);
+
+ public:
+ static void Init();
+ static uint32_t GetID();
+ static bool isBusy();
+ static void Abort() { /*__HAL_DMA_DISABLE(&DMAtx);*/ }
+
+ static void DataTransferBegin(uint16_t DataWidth = TFT_DATASIZE) {}
+ static void DataTransferEnd() {};
+
+ static void WriteData(uint16_t Data);
+ static void WriteReg(uint16_t Reg);
+
+ // Non-blocking DMA data transfer is not implemented for LTDC interface
+ inline static void WriteSequence_DMA(uint16_t *Data, uint16_t Count) { WriteSequence(Data, Count); }
+ inline static void WriteMultiple_DMA(uint16_t Color, uint16_t Count) { WriteMultiple(Color, Count); }
+
+ static void WriteSequence(uint16_t *Data, uint16_t Count) { Transmit(DMA_PINC_ENABLE, Data, Count); }
+ static void WriteMultiple(uint16_t Color, uint32_t Count) {
+ while (Count > 0) {
+ Transmit(DMA_PINC_DISABLE, &Color, Count > DMA_MAX_SIZE ? DMA_MAX_SIZE : Count);
+ Count = Count > DMA_MAX_SIZE ? Count - DMA_MAX_SIZE : 0;
+ }
+ }
+};
+
+const PinMap PinMap_LTDC[] = {
+ {PF_10, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_DE
+ {PG_7, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_CLK
+ {PI_9, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_VSYNC
+ {PI_10, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_HSYNC
+
+ {PG_6, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_R7
+ {PH_12, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_R6
+ {PH_11, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_R5
+ {PH_10, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_R4
+ {PH_9, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_R3
+
+ {PI_2, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_G7
+ {PI_1, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_G6
+ {PI_0, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_G5
+ {PH_15, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_G4
+ {PH_14, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_G3
+ {PH_13, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_G2
+
+ {PI_7, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_B7
+ {PI_6, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_B6
+ {PI_5, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_B5
+ {PI_4, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_B4
+ {PG_11, LTDC, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF14_LTDC)}, // LCD_B3
+ {NC, NP, 0}
+};
+
+const PinMap PinMap_SDRAM[] = {
+ {PC_0, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_SDNWE
+ {PC_2, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_SDNE0
+ {PC_3, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_SDCKE0
+ {PE_0, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_NBL0
+ {PE_1, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_NBL1
+ {PF_11, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_SDNRAS
+ {PG_8, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_SDCLK
+ {PG_15, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_SDNCAS
+ {PG_4, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_BA0
+ {PG_5, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_BA1
+ {PD_14, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D0
+ {PD_15, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D1
+ {PD_0, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D2
+ {PD_1, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D3
+ {PE_7, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D4
+ {PE_8, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D5
+ {PE_9, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D6
+ {PE_10, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D7
+ {PE_11, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D8
+ {PE_12, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D9
+ {PE_13, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D10
+ {PE_14, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D11
+ {PE_15, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D12
+ {PD_8, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D13
+ {PD_9, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D14
+ {PD_10, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_D15
+ {PF_0, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_A0
+ {PF_1, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_A1
+ {PF_2, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_A2
+ {PF_3, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_A3
+ {PF_4, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_A4
+ {PF_5, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_A5
+ {PF_12, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_A6
+ {PF_13, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_A7
+ {PF_14, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_A8
+ {PF_15, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_A9
+ {PG_0, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_A10
+ {PG_1, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_A11
+ {PG_2, FMC_Bank1_R, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_FMC)}, // FMC_A12
+ {NC, NP, 0}
+};
+
+const PinMap PinMap_QUADSPI[] = {
+ {PB_2, QUADSPI, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, // QUADSPI_CLK
+ {PB_10, QUADSPI, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, // QUADSPI_BK1_NCS
+ {PF_6, QUADSPI, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, // QUADSPI_BK1_IO3
+ {PF_7, QUADSPI, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, // QUADSPI_BK1_IO2
+ {PF_8, QUADSPI, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // QUADSPI_BK1_IO0
+ {PF_9, QUADSPI, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // QUADSPI_BK1_IO1
+ {NC, NP, 0}
+};
diff --git a/Marlin/src/HAL/STM32/tft/tft_spi.cpp b/Marlin/src/HAL/STM32/tft/tft_spi.cpp
new file mode 100644
index 0000000000..5e79f156d2
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/tft_spi.cpp
@@ -0,0 +1,290 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../../inc/MarlinConfig.h"
+
+#if HAS_SPI_TFT
+
+#include "tft_spi.h"
+#include "pinconfig.h"
+
+SPI_HandleTypeDef TFT_SPI::SPIx;
+DMA_HandleTypeDef TFT_SPI::DMAtx;
+
+void TFT_SPI::Init() {
+ SPI_TypeDef *spiInstance;
+
+ OUT_WRITE(TFT_A0_PIN, HIGH);
+ OUT_WRITE(TFT_CS_PIN, HIGH);
+
+ if ((spiInstance = (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TFT_SCK_PIN), PinMap_SPI_SCLK)) == NP) return;
+ if (spiInstance != (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TFT_MOSI_PIN), PinMap_SPI_MOSI)) return;
+
+ #if PIN_EXISTS(TFT_MISO) && TFT_MISO_PIN != TFT_MOSI_PIN
+ if (spiInstance != (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TFT_MISO_PIN), PinMap_SPI_MISO)) return;
+ #endif
+
+ SPIx.Instance = spiInstance;
+ SPIx.State = HAL_SPI_STATE_RESET;
+ SPIx.Init.NSS = SPI_NSS_SOFT;
+ SPIx.Init.Mode = SPI_MODE_MASTER;
+ SPIx.Init.Direction = (TFT_MISO_PIN == TFT_MOSI_PIN) ? SPI_DIRECTION_1LINE : SPI_DIRECTION_2LINES;
+ SPIx.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
+ SPIx.Init.CLKPhase = SPI_PHASE_1EDGE;
+ SPIx.Init.CLKPolarity = SPI_POLARITY_LOW;
+ SPIx.Init.DataSize = SPI_DATASIZE_8BIT;
+ SPIx.Init.FirstBit = SPI_FIRSTBIT_MSB;
+ SPIx.Init.TIMode = SPI_TIMODE_DISABLE;
+ SPIx.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
+ SPIx.Init.CRCPolynomial = 10;
+
+ pinmap_pinout(digitalPinToPinName(TFT_SCK_PIN), PinMap_SPI_SCLK);
+ pinmap_pinout(digitalPinToPinName(TFT_MOSI_PIN), PinMap_SPI_MOSI);
+ #if PIN_EXISTS(TFT_MISO) && TFT_MISO_PIN != TFT_MOSI_PIN
+ pinmap_pinout(digitalPinToPinName(TFT_MISO_PIN), PinMap_SPI_MISO);
+ #endif
+ pin_PullConfig(get_GPIO_Port(STM_PORT(digitalPinToPinName(TFT_SCK_PIN))), STM_LL_GPIO_PIN(digitalPinToPinName(TFT_SCK_PIN)), GPIO_PULLDOWN);
+
+ #ifdef SPI1_BASE
+ if (SPIx.Instance == SPI1) {
+ __HAL_RCC_SPI1_CLK_ENABLE();
+ #ifdef STM32F1xx
+ __HAL_RCC_DMA1_CLK_ENABLE();
+ DMAtx.Instance = DMA1_Channel3;
+ #elif defined(STM32F4xx)
+ __HAL_RCC_DMA2_CLK_ENABLE();
+ DMAtx.Instance = DMA2_Stream3;
+ DMAtx.Init.Channel = DMA_CHANNEL_3;
+ #endif
+ SPIx.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
+ }
+ #endif
+ #ifdef SPI2_BASE
+ if (SPIx.Instance == SPI2) {
+ __HAL_RCC_SPI2_CLK_ENABLE();
+ #ifdef STM32F1xx
+ __HAL_RCC_DMA1_CLK_ENABLE();
+ DMAtx.Instance = DMA1_Channel5;
+ #elif defined(STM32F4xx)
+ __HAL_RCC_DMA1_CLK_ENABLE();
+ DMAtx.Instance = DMA1_Stream4;
+ DMAtx.Init.Channel = DMA_CHANNEL_0;
+ #endif
+ }
+ #endif
+ #ifdef SPI3_BASE
+ if (SPIx.Instance == SPI3) {
+ __HAL_RCC_SPI3_CLK_ENABLE();
+ #ifdef STM32F1xx
+ __HAL_RCC_DMA2_CLK_ENABLE();
+ DMAtx.Instance = DMA2_Channel2;
+ #elif defined(STM32F4xx)
+ __HAL_RCC_DMA1_CLK_ENABLE();
+ DMAtx.Instance = DMA1_Stream5;
+ DMAtx.Init.Channel = DMA_CHANNEL_0;
+ #endif
+ }
+ #endif
+
+ HAL_SPI_Init(&SPIx);
+
+ DMAtx.Init.Direction = DMA_MEMORY_TO_PERIPH;
+ DMAtx.Init.PeriphInc = DMA_PINC_DISABLE;
+ DMAtx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
+ DMAtx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
+ DMAtx.Init.Mode = DMA_NORMAL;
+ DMAtx.Init.Priority = DMA_PRIORITY_LOW;
+ #ifdef STM32F4xx
+ DMAtx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
+ #endif
+}
+
+void TFT_SPI::DataTransferBegin(uint16_t DataSize) {
+ SPIx.Init.DataSize = DataSize == DATASIZE_8BIT ? SPI_DATASIZE_8BIT : SPI_DATASIZE_16BIT;
+ HAL_SPI_Init(&SPIx);
+ WRITE(TFT_CS_PIN, LOW);
+}
+
+#ifdef TFT_DEFAULT_DRIVER
+ #include "../../../lcd/tft_io/tft_ids.h"
+#endif
+
+uint32_t TFT_SPI::GetID() {
+ uint32_t id;
+ id = ReadID(LCD_READ_ID);
+ if ((id & 0xFFFF) == 0 || (id & 0xFFFF) == 0xFFFF) {
+ id = ReadID(LCD_READ_ID4);
+ #ifdef TFT_DEFAULT_DRIVER
+ if ((id & 0xFFFF) == 0 || (id & 0xFFFF) == 0xFFFF)
+ id = TFT_DEFAULT_DRIVER;
+ #endif
+ }
+ return id;
+}
+
+uint32_t TFT_SPI::ReadID(uint16_t Reg) {
+ uint32_t Data = 0;
+ #if PIN_EXISTS(TFT_MISO)
+ uint32_t BaudRatePrescaler = SPIx.Init.BaudRatePrescaler;
+ uint32_t i;
+
+ SPIx.Init.BaudRatePrescaler = SPIx.Instance == SPI1 ? SPI_BAUDRATEPRESCALER_8 : SPI_BAUDRATEPRESCALER_4;
+ DataTransferBegin(DATASIZE_8BIT);
+ WriteReg(Reg);
+
+ if (SPIx.Init.Direction == SPI_DIRECTION_1LINE) SPI_1LINE_RX(&SPIx);
+ __HAL_SPI_ENABLE(&SPIx);
+
+ for (i = 0; i < 4; i++) {
+ #if TFT_MISO_PIN != TFT_MOSI_PIN
+ while (!__HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_TXE)) {}
+ SPIx.Instance->DR = 0;
+ #endif
+ while (!__HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_RXNE)) {}
+ Data = (Data << 8) | SPIx.Instance->DR;
+ }
+
+ DataTransferEnd();
+
+ SPIx.Init.BaudRatePrescaler = BaudRatePrescaler;
+ #endif
+
+ return Data >> 7;
+}
+
+bool TFT_SPI::isBusy() {
+ #ifdef STM32F1xx
+ #define __IS_DMA_ENABLED(__HANDLE__) ((__HANDLE__)->Instance->CCR & DMA_CCR_EN)
+ #define __IS_DMA_CONFIGURED(__HANDLE__) ((__HANDLE__)->Instance->CPAR != 0)
+ #elif defined(STM32F4xx)
+ #define __IS_DMA_ENABLED(__HANDLE__) ((__HANDLE__)->Instance->CR & DMA_SxCR_EN)
+ #define __IS_DMA_CONFIGURED(__HANDLE__) ((__HANDLE__)->Instance->PAR != 0)
+ #endif
+
+ if (!__IS_DMA_CONFIGURED(&DMAtx)) return false;
+
+ if (__HAL_DMA_GET_FLAG(&DMAtx, __HAL_DMA_GET_TE_FLAG_INDEX(&DMAtx))) {
+ // You should not be here - DMA transfer error flag is set
+ // Abort DMA transfer and release SPI
+ }
+ else {
+ // Check if DMA transfer completed flag is set
+ if (__HAL_DMA_GET_FLAG(&DMAtx, __HAL_DMA_GET_TC_FLAG_INDEX(&DMAtx)) == 0) return true;
+ // Check if SPI transmit butter is empty and SPI is idle
+ if ((!__HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_TXE)) || (__HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_BSY))) return true;
+ }
+
+ Abort();
+ return false;
+}
+
+void TFT_SPI::Abort() {
+ HAL_DMA_Abort(&DMAtx); // Abort DMA transfer if any
+ HAL_DMA_DeInit(&DMAtx);
+
+ CLEAR_BIT(SPIx.Instance->CR2, SPI_CR2_TXDMAEN);
+
+ DataTransferEnd(); // Stop SPI and deselect CS
+}
+
+void TFT_SPI::Transmit(uint16_t Data) {
+ #if TFT_MISO_PIN == TFT_MOSI_PIN
+ SPI_1LINE_TX(&SPIx);
+ #endif
+
+ __HAL_SPI_ENABLE(&SPIx);
+
+ SPIx.Instance->DR = Data;
+
+ while (!__HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_TXE)) {}
+ while ( __HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_BSY)) {}
+
+ #if TFT_MISO_PIN != TFT_MOSI_PIN
+ __HAL_SPI_CLEAR_OVRFLAG(&SPIx); // Clear overrun flag in 2 Lines communication mode because received data is not read
+ #endif
+}
+
+void TFT_SPI::TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) {
+ DMAtx.Init.MemInc = MemoryIncrease;
+ HAL_DMA_Init(&DMAtx);
+
+ #if TFT_MISO_PIN == TFT_MOSI_PIN
+ SPI_1LINE_TX(&SPIx);
+ #endif
+
+ DataTransferBegin();
+
+ HAL_DMA_Start(&DMAtx, (uint32_t)Data, (uint32_t)&(SPIx.Instance->DR), Count);
+ __HAL_SPI_ENABLE(&SPIx);
+
+ SET_BIT(SPIx.Instance->CR2, SPI_CR2_TXDMAEN); // Enable Tx DMA Request
+
+ TERN_(TFT_SHARED_SPI, while (isBusy()));
+}
+
+
+void TFT_SPI::Transmit(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) {
+ DMAtx.Init.MemInc = MemoryIncrease;
+ HAL_DMA_Init(&DMAtx);
+
+ if (TFT_MISO_PIN == TFT_MOSI_PIN)
+ SPI_1LINE_TX(&SPIx);
+
+ DataTransferBegin();
+
+ HAL_DMA_Start(&DMAtx, (uint32_t)Data, (uint32_t)&(SPIx.Instance->DR), Count);
+ __HAL_SPI_ENABLE(&SPIx);
+
+ SET_BIT(SPIx.Instance->CR2, SPI_CR2_TXDMAEN); // Enable Tx DMA Request
+
+ HAL_DMA_PollForTransfer(&DMAtx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
+ Abort();
+}
+
+#if ENABLED(USE_SPI_DMA_TC)
+ void TFT_SPI::TransmitDMA_IT(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) {
+
+ DMAtx.Init.MemInc = MemoryIncrease;
+ HAL_DMA_Init(&DMAtx);
+
+ if (TFT_MISO_PIN == TFT_MOSI_PIN)
+ SPI_1LINE_TX(&SPIx);
+
+ DataTransferBegin();
+
+ HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 5, 0);
+ HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
+ HAL_DMA_Start_IT(&DMAtx, (uint32_t)Data, (uint32_t)&(SPIx.Instance->DR), Count);
+ __HAL_SPI_ENABLE(&SPIx);
+
+ SET_BIT(SPIx.Instance->CR2, SPI_CR2_TXDMAEN); // Enable Tx DMA Request
+ }
+
+ extern "C" void DMA2_Stream3_IRQHandler(void) { TFT_SPI::DMA_IRQHandler(); }
+#endif
+
+#endif // HAS_SPI_TFT
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/tft/tft_spi.h b/Marlin/src/HAL/STM32/tft/tft_spi.h
new file mode 100644
index 0000000000..6b8613e3f8
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/tft_spi.h
@@ -0,0 +1,85 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#ifdef STM32F1xx
+ #include "stm32f1xx_hal.h"
+#elif defined(STM32F4xx)
+ #include "stm32f4xx_hal.h"
+#else
+ #error SPI TFT is currently only supported on STM32F1 and STM32F4 hardware.
+#endif
+
+#ifndef LCD_READ_ID
+ #define LCD_READ_ID 0x04 // Read display identification information (0xD3 on ILI9341)
+#endif
+#ifndef LCD_READ_ID4
+ #define LCD_READ_ID4 0xD3 // Read display identification information (0xD3 on ILI9341)
+#endif
+
+#define DATASIZE_8BIT SPI_DATASIZE_8BIT
+#define DATASIZE_16BIT SPI_DATASIZE_16BIT
+#define TFT_IO_DRIVER TFT_SPI
+#define DMA_MAX_SIZE 0xFFFF
+
+class TFT_SPI {
+private:
+ static SPI_HandleTypeDef SPIx;
+ static DMA_HandleTypeDef DMAtx;
+
+ static uint32_t ReadID(uint16_t Reg);
+ static void Transmit(uint16_t Data);
+ static void Transmit(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count);
+ static void TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count);
+ #if ENABLED(USE_SPI_DMA_TC)
+ static void TransmitDMA_IT(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count);
+ #endif
+
+public:
+ static void Init();
+ static uint32_t GetID();
+ static bool isBusy();
+ static void Abort();
+
+ static void DataTransferBegin(uint16_t DataWidth = DATASIZE_16BIT);
+ static void DataTransferEnd() { WRITE(TFT_CS_PIN, HIGH); __HAL_SPI_DISABLE(&SPIx); };
+ static void DataTransferAbort();
+
+ static void WriteData(uint16_t Data) { Transmit(Data); }
+ static void WriteReg(uint16_t Reg) { WRITE(TFT_A0_PIN, LOW); Transmit(Reg); WRITE(TFT_A0_PIN, HIGH); }
+
+ static void WriteSequence_DMA(uint16_t *Data, uint16_t Count) { TransmitDMA(DMA_MINC_ENABLE, Data, Count); }
+ static void WriteMultiple_DMA(uint16_t Color, uint16_t Count) { static uint16_t Data; Data = Color; TransmitDMA(DMA_MINC_DISABLE, &Data, Count); }
+
+ #if ENABLED(USE_SPI_DMA_TC)
+ static void WriteSequenceIT(uint16_t *Data, uint16_t Count) { TransmitDMA_IT(DMA_MINC_ENABLE, Data, Count); }
+ inline static void DMA_IRQHandler() { HAL_DMA_IRQHandler(&TFT_SPI::DMAtx); }
+ #endif
+
+ static void WriteSequence(uint16_t *Data, uint16_t Count) { Transmit(DMA_MINC_ENABLE, Data, Count); }
+ static void WriteMultiple(uint16_t Color, uint32_t Count) {
+ while (Count > 0) {
+ Transmit(DMA_MINC_DISABLE, &Color, Count > DMA_MAX_SIZE ? DMA_MAX_SIZE : Count);
+ Count = Count > DMA_MAX_SIZE ? Count - DMA_MAX_SIZE : 0;
+ }
+ }
+};
diff --git a/Marlin/src/HAL/STM32/tft/xpt2046.cpp b/Marlin/src/HAL/STM32/tft/xpt2046.cpp
new file mode 100644
index 0000000000..cf4a8f18e9
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/xpt2046.cpp
@@ -0,0 +1,173 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../../inc/MarlinConfig.h"
+
+#if HAS_TFT_XPT2046 || HAS_RES_TOUCH_BUTTONS
+
+#include "xpt2046.h"
+#include "pinconfig.h"
+
+uint16_t delta(uint16_t a, uint16_t b) { return a > b ? a - b : b - a; }
+
+SPI_HandleTypeDef XPT2046::SPIx;
+
+void XPT2046::Init() {
+ SPI_TypeDef *spiInstance;
+
+ OUT_WRITE(TOUCH_CS_PIN, HIGH);
+
+ #if PIN_EXISTS(TOUCH_INT)
+ // Optional Pendrive interrupt pin
+ SET_INPUT(TOUCH_INT_PIN);
+ #endif
+
+ spiInstance = (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TOUCH_SCK_PIN), PinMap_SPI_SCLK);
+ if (spiInstance != (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TOUCH_MOSI_PIN), PinMap_SPI_MOSI)) spiInstance = NP;
+ if (spiInstance != (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TOUCH_MISO_PIN), PinMap_SPI_MISO)) spiInstance = NP;
+
+ SPIx.Instance = spiInstance;
+
+ if (SPIx.Instance) {
+ SPIx.State = HAL_SPI_STATE_RESET;
+ SPIx.Init.NSS = SPI_NSS_SOFT;
+ SPIx.Init.Mode = SPI_MODE_MASTER;
+ SPIx.Init.Direction = SPI_DIRECTION_2LINES;
+ SPIx.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
+ SPIx.Init.CLKPhase = SPI_PHASE_2EDGE;
+ SPIx.Init.CLKPolarity = SPI_POLARITY_HIGH;
+ SPIx.Init.DataSize = SPI_DATASIZE_8BIT;
+ SPIx.Init.FirstBit = SPI_FIRSTBIT_MSB;
+ SPIx.Init.TIMode = SPI_TIMODE_DISABLE;
+ SPIx.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
+ SPIx.Init.CRCPolynomial = 10;
+
+ pinmap_pinout(digitalPinToPinName(TOUCH_SCK_PIN), PinMap_SPI_SCLK);
+ pinmap_pinout(digitalPinToPinName(TOUCH_MOSI_PIN), PinMap_SPI_MOSI);
+ pinmap_pinout(digitalPinToPinName(TOUCH_MISO_PIN), PinMap_SPI_MISO);
+
+ #ifdef SPI1_BASE
+ if (SPIx.Instance == SPI1) {
+ __HAL_RCC_SPI1_CLK_ENABLE();
+ SPIx.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
+ }
+ #endif
+ #ifdef SPI2_BASE
+ if (SPIx.Instance == SPI2) {
+ __HAL_RCC_SPI2_CLK_ENABLE();
+ }
+ #endif
+ #ifdef SPI3_BASE
+ if (SPIx.Instance == SPI3) {
+ __HAL_RCC_SPI3_CLK_ENABLE();
+ }
+ #endif
+ }
+ else {
+ SPIx.Instance = nullptr;
+ SET_INPUT(TOUCH_MISO_PIN);
+ SET_OUTPUT(TOUCH_MOSI_PIN);
+ SET_OUTPUT(TOUCH_SCK_PIN);
+ }
+
+ getRawData(XPT2046_Z1);
+}
+
+bool XPT2046::isTouched() {
+ return isBusy() ? false : (
+ #if PIN_EXISTS(TOUCH_INT)
+ READ(TOUCH_INT_PIN) != HIGH
+ #else
+ getRawData(XPT2046_Z1) >= XPT2046_Z1_THRESHOLD
+ #endif
+ );
+}
+
+bool XPT2046::getRawPoint(int16_t *x, int16_t *y) {
+ if (isBusy()) return false;
+ if (!isTouched()) return false;
+ *x = getRawData(XPT2046_X);
+ *y = getRawData(XPT2046_Y);
+ return isTouched();
+}
+
+uint16_t XPT2046::getRawData(const XPTCoordinate coordinate) {
+ uint16_t data[3];
+
+ DataTransferBegin();
+
+ for (uint16_t i = 0; i < 3 ; i++) {
+ IO(coordinate);
+ data[i] = (IO() << 4) | (IO() >> 4);
+ }
+
+ DataTransferEnd();
+
+ uint16_t delta01 = delta(data[0], data[1]);
+ uint16_t delta02 = delta(data[0], data[2]);
+ uint16_t delta12 = delta(data[1], data[2]);
+
+ if (delta01 > delta02 || delta01 > delta12) {
+ if (delta02 > delta12)
+ data[0] = data[2];
+ else
+ data[1] = data[2];
+ }
+
+ return (data[0] + data[1]) >> 1;
+}
+
+uint16_t XPT2046::HardwareIO(uint16_t data) {
+ __HAL_SPI_ENABLE(&SPIx);
+ while ((SPIx.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE) {}
+ SPIx.Instance->DR = data;
+ while ((SPIx.Instance->SR & SPI_FLAG_RXNE) != SPI_FLAG_RXNE) {}
+ __HAL_SPI_DISABLE(&SPIx);
+
+ return SPIx.Instance->DR;
+}
+
+uint16_t XPT2046::SoftwareIO(uint16_t data) {
+ uint16_t result = 0;
+
+ for (uint8_t j = 0x80; j > 0; j >>= 1) {
+ WRITE(TOUCH_SCK_PIN, LOW);
+ __DSB();
+ WRITE(TOUCH_MOSI_PIN, data & j ? HIGH : LOW);
+ __DSB();
+ if (READ(TOUCH_MISO_PIN)) result |= j;
+ __DSB();
+ WRITE(TOUCH_SCK_PIN, HIGH);
+ __DSB();
+ }
+ WRITE(TOUCH_SCK_PIN, LOW);
+ __DSB();
+
+ return result;
+}
+
+#endif // HAS_TFT_XPT2046
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/tft/xpt2046.h b/Marlin/src/HAL/STM32/tft/xpt2046.h
new file mode 100644
index 0000000000..71de6b0025
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/xpt2046.h
@@ -0,0 +1,81 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#ifdef STM32F1xx
+ #include
+#elif defined(STM32F4xx)
+ #include
+#endif
+
+#include "../../../inc/MarlinConfig.h"
+
+// Not using regular SPI interface by default to avoid SPI mode conflicts with other SPI devices
+
+#if !PIN_EXISTS(TOUCH_MISO)
+ #error "TOUCH_MISO_PIN is not defined."
+#elif !PIN_EXISTS(TOUCH_MOSI)
+ #error "TOUCH_MOSI_PIN is not defined."
+#elif !PIN_EXISTS(TOUCH_SCK)
+ #error "TOUCH_SCK_PIN is not defined."
+#elif !PIN_EXISTS(TOUCH_CS)
+ #error "TOUCH_CS_PIN is not defined."
+#endif
+
+#ifndef TOUCH_INT_PIN
+ #define TOUCH_INT_PIN -1
+#endif
+
+#define XPT2046_DFR_MODE 0x00
+#define XPT2046_SER_MODE 0x04
+#define XPT2046_CONTROL 0x80
+
+enum XPTCoordinate : uint8_t {
+ XPT2046_X = 0x10 | XPT2046_CONTROL | XPT2046_DFR_MODE,
+ XPT2046_Y = 0x50 | XPT2046_CONTROL | XPT2046_DFR_MODE,
+ XPT2046_Z1 = 0x30 | XPT2046_CONTROL | XPT2046_DFR_MODE,
+ XPT2046_Z2 = 0x40 | XPT2046_CONTROL | XPT2046_DFR_MODE,
+};
+
+#ifndef XPT2046_Z1_THRESHOLD
+ #define XPT2046_Z1_THRESHOLD 10
+#endif
+
+class XPT2046 {
+private:
+ static SPI_HandleTypeDef SPIx;
+
+ static bool isBusy() { return false; }
+
+ static uint16_t getRawData(const XPTCoordinate coordinate);
+ static bool isTouched();
+
+ static void DataTransferBegin() { if (SPIx.Instance) { HAL_SPI_Init(&SPIx); } WRITE(TOUCH_CS_PIN, LOW); };
+ static void DataTransferEnd() { WRITE(TOUCH_CS_PIN, HIGH); };
+ static uint16_t HardwareIO(uint16_t data);
+ static uint16_t SoftwareIO(uint16_t data);
+ static uint16_t IO(uint16_t data = 0) { return SPIx.Instance ? HardwareIO(data) : SoftwareIO(data); }
+
+public:
+ static void Init();
+ static bool getRawPoint(int16_t *x, int16_t *y);
+};
diff --git a/Marlin/src/HAL/STM32/timers.cpp b/Marlin/src/HAL/STM32/timers.cpp
new file mode 100644
index 0000000000..e68b59c46f
--- /dev/null
+++ b/Marlin/src/HAL/STM32/timers.cpp
@@ -0,0 +1,330 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
+ *
+ * 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 "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfig.h"
+
+// ------------------------
+// Local defines
+// ------------------------
+
+// Default timer priorities. Override by specifying alternate priorities in the board pins file.
+// The TONE timer is not present here, as it currently cannot be set programmatically. It is set
+// by defining TIM_IRQ_PRIO in the variant.h or platformio.ini file, which adjusts the default
+// priority for STM32 HardwareTimer objects.
+#define SWSERIAL_TIMER_IRQ_PRIO_DEFAULT 1 // Requires tight bit timing to communicate reliably with TMC drivers
+#define SERVO_TIMER_IRQ_PRIO_DEFAULT 1 // Requires tight PWM timing to control a BLTouch reliably
+#define STEP_TIMER_IRQ_PRIO_DEFAULT 2
+#define TEMP_TIMER_IRQ_PRIO_DEFAULT 14 // Low priority avoids interference with other hardware and timers
+
+#ifndef STEP_TIMER_IRQ_PRIO
+ #define STEP_TIMER_IRQ_PRIO STEP_TIMER_IRQ_PRIO_DEFAULT
+#endif
+#ifndef TEMP_TIMER_IRQ_PRIO
+ #define TEMP_TIMER_IRQ_PRIO TEMP_TIMER_IRQ_PRIO_DEFAULT
+#endif
+#if HAS_TMC_SW_SERIAL
+ #include
+ #ifndef SWSERIAL_TIMER_IRQ_PRIO
+ #define SWSERIAL_TIMER_IRQ_PRIO SWSERIAL_TIMER_IRQ_PRIO_DEFAULT
+ #endif
+#endif
+#if HAS_SERVOS
+ #include "Servo.h"
+ #ifndef SERVO_TIMER_IRQ_PRIO
+ #define SERVO_TIMER_IRQ_PRIO SERVO_TIMER_IRQ_PRIO_DEFAULT
+ #endif
+#endif
+#if ENABLED(SPEAKER)
+ // Ensure the default timer priority is somewhere between the STEP and TEMP priorities.
+ // The STM32 framework defaults to interrupt 14 for all timers. This should be increased so that
+ // timing-sensitive operations such as speaker output are not impacted by the long-running
+ // temperature ISR. This must be defined in the platformio.ini file or the board's variant.h,
+ // so that it will be consumed by framework code.
+ #if !(TIM_IRQ_PRIO > STEP_TIMER_IRQ_PRIO && TIM_IRQ_PRIO < TEMP_TIMER_IRQ_PRIO)
+ #error "Default timer interrupt priority is unspecified or set to a value which may degrade performance."
+ #endif
+#endif
+
+#if defined(STM32F0xx) || defined(STM32G0xx)
+ #define MCU_STEP_TIMER 16
+ #define MCU_TEMP_TIMER 17
+#elif defined(STM32F1xx)
+ #define MCU_STEP_TIMER 4
+ #define MCU_TEMP_TIMER 2
+#elif defined(STM32F401xC) || defined(STM32F401xE)
+ #define MCU_STEP_TIMER 9 // STM32F401 has no TIM6, TIM7, or TIM8
+ #define MCU_TEMP_TIMER 10
+#elif defined(STM32F4xx) || defined(STM32F7xx) || defined(STM32H7xx)
+ #define MCU_STEP_TIMER 6
+ #define MCU_TEMP_TIMER 14 // TIM7 is consumed by Software Serial if used.
+#endif
+
+#ifndef HAL_TIMER_RATE
+ #define HAL_TIMER_RATE GetStepperTimerClkFreq()
+#endif
+
+#ifndef STEP_TIMER
+ #define STEP_TIMER MCU_STEP_TIMER
+#endif
+#ifndef TEMP_TIMER
+ #define TEMP_TIMER MCU_TEMP_TIMER
+#endif
+
+#define __TIMER_DEV(X) TIM##X
+#define _TIMER_DEV(X) __TIMER_DEV(X)
+#define STEP_TIMER_DEV _TIMER_DEV(STEP_TIMER)
+#define TEMP_TIMER_DEV _TIMER_DEV(TEMP_TIMER)
+
+// --------------------------------------------------------------------------
+// Local defines
+// --------------------------------------------------------------------------
+
+#define NUM_HARDWARE_TIMERS 2
+
+// --------------------------------------------------------------------------
+// Private Variables
+// --------------------------------------------------------------------------
+
+HardwareTimer *timer_instance[NUM_HARDWARE_TIMERS] = { nullptr };
+
+// ------------------------
+// Public functions
+// ------------------------
+
+uint32_t GetStepperTimerClkFreq() {
+ // Timer input clocks vary between devices, and in some cases between timers on the same device.
+ // Retrieve at runtime to ensure device compatibility. Cache result to avoid repeated overhead.
+ static uint32_t clkfreq = timer_instance[MF_TIMER_STEP]->getTimerClkFreq();
+ return clkfreq;
+}
+
+// frequency is in Hertz
+void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) {
+ if (!HAL_timer_initialized(timer_num)) {
+ switch (timer_num) {
+ case MF_TIMER_STEP: // STEPPER TIMER - use a 32bit timer if possible
+ timer_instance[timer_num] = new HardwareTimer(STEP_TIMER_DEV);
+ /* Set the prescaler to the final desired value.
+ * This will change the effective ISR callback frequency but when
+ * HAL_timer_start(timer_num=0) is called in the core for the first time
+ * the real frequency isn't important as long as, after boot, the ISR
+ * gets called with the correct prescaler and count register. So here
+ * we set the prescaler to the correct, final value and ignore the frequency
+ * asked. We will call back the ISR in 1 second to start at full speed.
+ *
+ * The proper fix, however, would be a correct initialization OR a
+ * HAL_timer_change(const uint8_t timer_num, const uint32_t frequency)
+ * which changes the prescaler when an IRQ frequency change is needed
+ * (for example when steppers are turned on)
+ */
+
+ timer_instance[timer_num]->setPrescaleFactor(STEPPER_TIMER_PRESCALE); //the -1 is done internally
+ timer_instance[timer_num]->setOverflow(_MIN(hal_timer_t(HAL_TIMER_TYPE_MAX), (HAL_TIMER_RATE) / (STEPPER_TIMER_PRESCALE) /* /frequency */), TICK_FORMAT);
+ break;
+ case MF_TIMER_TEMP: // TEMP TIMER - any available 16bit timer
+ timer_instance[timer_num] = new HardwareTimer(TEMP_TIMER_DEV);
+ // The prescale factor is computed automatically for HERTZ_FORMAT
+ timer_instance[timer_num]->setOverflow(frequency, HERTZ_FORMAT);
+ break;
+ }
+
+ // Disable preload. Leaving it default-enabled can cause the timer to stop if it happens
+ // to exit the ISR after the start time for the next interrupt has already passed.
+ timer_instance[timer_num]->setPreloadEnable(false);
+
+ HAL_timer_enable_interrupt(timer_num);
+
+ // Start the timer.
+ timer_instance[timer_num]->resume(); // First call to resume() MUST follow the attachInterrupt()
+
+ // This is fixed in Arduino_Core_STM32 1.8.
+ // These calls can be removed and replaced with
+ // timer_instance[timer_num]->setInterruptPriority
+ switch (timer_num) {
+ case MF_TIMER_STEP:
+ timer_instance[timer_num]->setInterruptPriority(STEP_TIMER_IRQ_PRIO, 0);
+ break;
+ case MF_TIMER_TEMP:
+ timer_instance[timer_num]->setInterruptPriority(TEMP_TIMER_IRQ_PRIO, 0);
+ break;
+ }
+ }
+}
+
+void HAL_timer_enable_interrupt(const uint8_t timer_num) {
+ if (HAL_timer_initialized(timer_num) && !timer_instance[timer_num]->hasInterrupt()) {
+ switch (timer_num) {
+ case MF_TIMER_STEP:
+ timer_instance[timer_num]->attachInterrupt(Step_Handler);
+ break;
+ case MF_TIMER_TEMP:
+ timer_instance[timer_num]->attachInterrupt(Temp_Handler);
+ break;
+ }
+ }
+}
+
+void HAL_timer_disable_interrupt(const uint8_t timer_num) {
+ if (HAL_timer_initialized(timer_num)) timer_instance[timer_num]->detachInterrupt();
+}
+
+bool HAL_timer_interrupt_enabled(const uint8_t timer_num) {
+ return HAL_timer_initialized(timer_num) && timer_instance[timer_num]->hasInterrupt();
+}
+
+void SetTimerInterruptPriorities() {
+ TERN_(HAS_TMC_SW_SERIAL, SoftwareSerial::setInterruptPriority(SWSERIAL_TIMER_IRQ_PRIO, 0));
+ TERN_(HAS_SERVOS, libServo::setInterruptPriority(SERVO_TIMER_IRQ_PRIO, 0));
+}
+
+// ------------------------
+// Detect timer conflicts
+// ------------------------
+
+// This list serves two purposes. Firstly, it facilitates build-time mapping between
+// variant-defined timer names (such as TIM1) and timer numbers. It also replicates
+// the order of timers used in the framework's SoftwareSerial.cpp. The first timer in
+// this list will be automatically used by SoftwareSerial if it is not already defined
+// in the board's variant or compiler options.
+static constexpr struct {uintptr_t base_address; int timer_number;} stm32_timer_map[] = {
+ #ifdef TIM18_BASE
+ { uintptr_t(TIM18), 18 },
+ #endif
+ #ifdef TIM7_BASE
+ { uintptr_t(TIM7), 7 },
+ #endif
+ #ifdef TIM6_BASE
+ { uintptr_t(TIM6), 6 },
+ #endif
+ #ifdef TIM22_BASE
+ { uintptr_t(TIM22), 22 },
+ #endif
+ #ifdef TIM21_BASE
+ { uintptr_t(TIM21), 21 },
+ #endif
+ #ifdef TIM17_BASE
+ { uintptr_t(TIM17), 17 },
+ #endif
+ #ifdef TIM16_BASE
+ { uintptr_t(TIM16), 16 },
+ #endif
+ #ifdef TIM15_BASE
+ { uintptr_t(TIM15), 15 },
+ #endif
+ #ifdef TIM14_BASE
+ { uintptr_t(TIM14), 14 },
+ #endif
+ #ifdef TIM13_BASE
+ { uintptr_t(TIM13), 13 },
+ #endif
+ #ifdef TIM11_BASE
+ { uintptr_t(TIM11), 11 },
+ #endif
+ #ifdef TIM10_BASE
+ { uintptr_t(TIM10), 10 },
+ #endif
+ #ifdef TIM12_BASE
+ { uintptr_t(TIM12), 12 },
+ #endif
+ #ifdef TIM19_BASE
+ { uintptr_t(TIM19), 19 },
+ #endif
+ #ifdef TIM9_BASE
+ { uintptr_t(TIM9), 9 },
+ #endif
+ #ifdef TIM5_BASE
+ { uintptr_t(TIM5), 5 },
+ #endif
+ #ifdef TIM4_BASE
+ { uintptr_t(TIM4), 4 },
+ #endif
+ #ifdef TIM3_BASE
+ { uintptr_t(TIM3), 3 },
+ #endif
+ #ifdef TIM2_BASE
+ { uintptr_t(TIM2), 2 },
+ #endif
+ #ifdef TIM20_BASE
+ { uintptr_t(TIM20), 20 },
+ #endif
+ #ifdef TIM8_BASE
+ { uintptr_t(TIM8), 8 },
+ #endif
+ #ifdef TIM1_BASE
+ { uintptr_t(TIM1), 1 }
+ #endif
+};
+
+// Convert from a timer base address to its integer timer number.
+static constexpr int get_timer_num_from_base_address(uintptr_t base_address) {
+ for (const auto &timer : stm32_timer_map)
+ if (timer.base_address == base_address) return timer.timer_number;
+ return 0;
+}
+
+// The platform's SoftwareSerial.cpp will use the first timer from stm32_timer_map.
+#if HAS_TMC_SW_SERIAL && !defined(TIMER_SERIAL)
+ #define TIMER_SERIAL (stm32_timer_map[0].base_address)
+#endif
+
+// constexpr doesn't like using the base address pointers that timers evaluate to.
+// We can get away with casting them to uintptr_t, if we do so inside an array.
+// GCC will not currently do it directly to a uintptr_t.
+IF_ENABLED(HAS_TMC_SW_SERIAL, static constexpr uintptr_t timer_serial[] = {uintptr_t(TIMER_SERIAL)});
+IF_ENABLED(SPEAKER, static constexpr uintptr_t timer_tone[] = {uintptr_t(TIMER_TONE)});
+IF_ENABLED(HAS_SERVOS, static constexpr uintptr_t timer_servo[] = {uintptr_t(TIMER_SERVO)});
+
+enum TimerPurpose { TP_SERIAL, TP_TONE, TP_SERVO, TP_STEP, TP_TEMP };
+
+// List of timers, to enable checking for conflicts.
+// Includes the purpose of each timer to ease debugging when evaluating at build-time.
+// This cannot yet account for timers used for PWM output, such as for fans.
+static constexpr struct { TimerPurpose p; int t; } timers_in_use[] = {
+ #if HAS_TMC_SW_SERIAL
+ { TP_SERIAL, get_timer_num_from_base_address(timer_serial[0]) }, // Set in variant.h, or as a define in platformio.h if not present in variant.h
+ #endif
+ #if ENABLED(SPEAKER)
+ { TP_TONE, get_timer_num_from_base_address(timer_tone[0]) }, // Set in variant.h, or as a define in platformio.h if not present in variant.h
+ #endif
+ #if HAS_SERVOS
+ { TP_SERVO, get_timer_num_from_base_address(timer_servo[0]) }, // Set in variant.h, or as a define in platformio.h if not present in variant.h
+ #endif
+ { TP_STEP, STEP_TIMER },
+ { TP_TEMP, TEMP_TIMER },
+};
+
+static constexpr bool verify_no_timer_conflicts() {
+ LOOP_L_N(i, COUNT(timers_in_use))
+ LOOP_S_L_N(j, i + 1, COUNT(timers_in_use))
+ if (timers_in_use[i].t == timers_in_use[j].t) return false;
+ return true;
+}
+
+// If this assertion fails at compile time, review the timers_in_use array.
+// If default_envs is defined properly in platformio.ini, VS Code can evaluate the array
+// when hovering over it, making it easy to identify the conflicting timers.
+static_assert(verify_no_timer_conflicts(), "One or more timer conflict detected. Examine \"timers_in_use\" to help identify conflict.");
+
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/timers.h b/Marlin/src/HAL/STM32/timers.h
new file mode 100644
index 0000000000..6828998198
--- /dev/null
+++ b/Marlin/src/HAL/STM32/timers.h
@@ -0,0 +1,120 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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/MarlinConfig.h"
+
+// ------------------------
+// Defines
+// ------------------------
+
+// STM32 timers may be 16 or 32 bit. Limiting HAL_TIMER_TYPE_MAX to 16 bits
+// avoids issues with STM32F0 MCUs, which seem to pause timers if UINT32_MAX
+// is written to the register. STM32F4 timers do not manifest this issue,
+// even when writing to 16 bit timers.
+//
+// The range of the timer can be queried at runtime using IS_TIM_32B_COUNTER_INSTANCE.
+// This is a more expensive check than a simple compile-time constant, so its
+// implementation is deferred until the desire for a 32-bit range outweighs the cost
+// of adding a run-time check and HAL_TIMER_TYPE_MAX is refactored to allow unique
+// values for each timer.
+#define hal_timer_t uint32_t
+#define HAL_TIMER_TYPE_MAX UINT16_MAX
+
+// Marlin timer_instance[] content (unrelated to timer selection)
+#define MF_TIMER_STEP 0 // Timer Index for Stepper
+#define MF_TIMER_TEMP 1 // Timer Index for Temperature
+#define MF_TIMER_PULSE MF_TIMER_STEP
+
+#define TIMER_INDEX_(T) TIMER##T##_INDEX // TIMER#_INDEX enums (timer_index_t) depend on TIM#_BASE defines.
+#define TIMER_INDEX(T) TIMER_INDEX_(T) // Convert Timer ID to HardwareTimer_Handle index.
+
+#define TEMP_TIMER_FREQUENCY 1000 // Temperature::isr() is expected to be called at around 1kHz
+
+// TODO: get rid of manual rate/prescale/ticks/cycles taken for procedures in stepper.cpp
+#define STEPPER_TIMER_RATE 2000000 // 2 Mhz
+extern uint32_t GetStepperTimerClkFreq();
+#define STEPPER_TIMER_PRESCALE (GetStepperTimerClkFreq() / (STEPPER_TIMER_RATE))
+#define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) // stepper timer ticks per µs
+
+#define PULSE_TIMER_RATE STEPPER_TIMER_RATE
+#define PULSE_TIMER_PRESCALE STEPPER_TIMER_PRESCALE
+#define PULSE_TIMER_TICKS_PER_US STEPPER_TIMER_TICKS_PER_US
+
+#define ENABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_STEP)
+#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_STEP)
+#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(MF_TIMER_STEP)
+
+#define ENABLE_TEMPERATURE_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_TEMP)
+#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_TEMP)
+
+extern void Step_Handler();
+extern void Temp_Handler();
+
+#ifndef HAL_STEP_TIMER_ISR
+ #define HAL_STEP_TIMER_ISR() void Step_Handler()
+#endif
+#ifndef HAL_TEMP_TIMER_ISR
+ #define HAL_TEMP_TIMER_ISR() void Temp_Handler()
+#endif
+
+// ------------------------
+// Public Variables
+// ------------------------
+
+extern HardwareTimer *timer_instance[];
+
+// ------------------------
+// Public functions
+// ------------------------
+
+void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency);
+void HAL_timer_enable_interrupt(const uint8_t timer_num);
+void HAL_timer_disable_interrupt(const uint8_t timer_num);
+bool HAL_timer_interrupt_enabled(const uint8_t timer_num);
+
+// Configure timer priorities for peripherals such as Software Serial or Servos.
+// Exposed here to allow all timer priority information to reside in timers.cpp
+void SetTimerInterruptPriorities();
+
+// FORCE_INLINE because these are used in performance-critical situations
+FORCE_INLINE bool HAL_timer_initialized(const uint8_t timer_num) {
+ return timer_instance[timer_num] != nullptr;
+}
+FORCE_INLINE static hal_timer_t HAL_timer_get_count(const uint8_t timer_num) {
+ return HAL_timer_initialized(timer_num) ? timer_instance[timer_num]->getCount() : 0;
+}
+
+// NOTE: Method name may be misleading.
+// STM32 has an Auto-Reload Register (ARR) as opposed to a "compare" register
+FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, const hal_timer_t overflow) {
+ if (HAL_timer_initialized(timer_num)) {
+ timer_instance[timer_num]->setOverflow(overflow + 1, TICK_FORMAT); // Value decremented by setOverflow()
+ // wiki: "force all registers (Autoreload, prescaler, compare) to be taken into account"
+ // So, if the new overflow value is less than the count it will trigger a rollover interrupt.
+ if (overflow < timer_instance[timer_num]->getCount()) // Added 'if' here because reports say it won't boot without it
+ timer_instance[timer_num]->refresh();
+ }
+}
+
+#define HAL_timer_isr_prologue(T) NOOP
+#define HAL_timer_isr_epilogue(T) NOOP
diff --git a/Marlin/src/HAL/STM32/usb_host.cpp b/Marlin/src/HAL/STM32/usb_host.cpp
new file mode 100644
index 0000000000..d77f0b28e9
--- /dev/null
+++ b/Marlin/src/HAL/STM32/usb_host.cpp
@@ -0,0 +1,119 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfig.h"
+
+#if BOTH(USE_OTG_USB_HOST, USBHOST)
+
+#include "usb_host.h"
+#include "../shared/Marduino.h"
+#include "usbh_core.h"
+#include "usbh_msc.h"
+
+USBH_HandleTypeDef hUsbHost;
+USBHost usb;
+BulkStorage bulk(&usb);
+
+static void USBH_UserProcess(USBH_HandleTypeDef *phost, uint8_t id) {
+ switch(id) {
+ case HOST_USER_SELECT_CONFIGURATION:
+ //SERIAL_ECHOLNPGM("APPLICATION_SELECT_CONFIGURATION");
+ break;
+ case HOST_USER_DISCONNECTION:
+ //SERIAL_ECHOLNPGM("APPLICATION_DISCONNECT");
+ //usb.setUsbTaskState(USB_STATE_RUNNING);
+ break;
+ case HOST_USER_CLASS_ACTIVE:
+ //SERIAL_ECHOLNPGM("APPLICATION_READY");
+ usb.setUsbTaskState(USB_STATE_RUNNING);
+ break;
+ case HOST_USER_CONNECTION:
+ break;
+ default:
+ break;
+ }
+}
+
+bool USBHost::start() {
+ if (USBH_Init(&hUsbHost, USBH_UserProcess, TERN(USE_USB_HS_IN_FS, HOST_HS, HOST_FS)) != USBH_OK) {
+ SERIAL_ECHOLNPGM("Error: USBH_Init");
+ return false;
+ }
+ if (USBH_RegisterClass(&hUsbHost, USBH_MSC_CLASS) != USBH_OK) {
+ SERIAL_ECHOLNPGM("Error: USBH_RegisterClass");
+ return false;
+ }
+ if (USBH_Start(&hUsbHost) != USBH_OK) {
+ SERIAL_ECHOLNPGM("Error: USBH_Start");
+ return false;
+ }
+ return true;
+}
+
+void USBHost::Task() {
+ USBH_Process(&hUsbHost);
+}
+
+uint8_t USBHost::getUsbTaskState() {
+ return usb_task_state;
+}
+
+void USBHost::setUsbTaskState(uint8_t state) {
+ usb_task_state = state;
+ if (usb_task_state == USB_STATE_RUNNING) {
+ MSC_LUNTypeDef info;
+ USBH_MSC_GetLUNInfo(&hUsbHost, usb.lun, &info);
+ capacity = info.capacity.block_nbr / 2000;
+ block_size = info.capacity.block_size;
+ block_count = info.capacity.block_nbr;
+ //SERIAL_ECHOLNPGM("info.capacity.block_nbr : %ld\n", info.capacity.block_nbr);
+ //SERIAL_ECHOLNPGM("info.capacity.block_size: %d\n", info.capacity.block_size);
+ //SERIAL_ECHOLNPGM("capacity : %d MB\n", capacity);
+ }
+};
+
+bool BulkStorage::LUNIsGood(uint8_t t) {
+ return USBH_MSC_IsReady(&hUsbHost) && USBH_MSC_UnitIsReady(&hUsbHost, t);
+}
+
+uint32_t BulkStorage::GetCapacity(uint8_t lun) {
+ return usb->block_count;
+}
+
+uint16_t BulkStorage::GetSectorSize(uint8_t lun) {
+ return usb->block_size;
+}
+
+uint8_t BulkStorage::Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf) {
+ return USBH_MSC_Read(&hUsbHost, lun, addr, buf, blocks) != USBH_OK;
+}
+
+uint8_t BulkStorage::Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t * buf) {
+ return USBH_MSC_Write(&hUsbHost, lun, addr, const_cast(buf), blocks) != USBH_OK;
+}
+
+#endif // USE_OTG_USB_HOST && USBHOST
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/usb_host.h b/Marlin/src/HAL/STM32/usb_host.h
new file mode 100644
index 0000000000..c0001c0d75
--- /dev/null
+++ b/Marlin/src/HAL/STM32/usb_host.h
@@ -0,0 +1,60 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include
+
+typedef enum {
+ USB_STATE_INIT,
+ USB_STATE_ERROR,
+ USB_STATE_RUNNING,
+} usb_state_t;
+
+class USBHost {
+public:
+ bool start();
+ void Task();
+ uint8_t getUsbTaskState();
+ void setUsbTaskState(uint8_t state);
+ uint8_t regRd(uint8_t reg) { return 0x0; };
+ uint8_t usb_task_state = USB_STATE_INIT;
+ uint8_t lun = 0;
+ uint32_t capacity = 0;
+ uint16_t block_size = 0;
+ uint32_t block_count = 0;
+};
+
+class BulkStorage {
+public:
+ BulkStorage(USBHost *usb) : usb(usb) {};
+
+ bool LUNIsGood(uint8_t t);
+ uint32_t GetCapacity(uint8_t lun);
+ uint16_t GetSectorSize(uint8_t lun);
+ uint8_t Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf);
+ uint8_t Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t * buf);
+
+ USBHost *usb;
+};
+
+extern USBHost usb;
+extern BulkStorage bulk;
diff --git a/Marlin/src/HAL/STM32/usb_serial.cpp b/Marlin/src/HAL/STM32/usb_serial.cpp
new file mode 100644
index 0000000000..0b2372f3a7
--- /dev/null
+++ b/Marlin/src/HAL/STM32/usb_serial.cpp
@@ -0,0 +1,60 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../platforms.h"
+
+#ifdef HAL_STM32
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if ENABLED(EMERGENCY_PARSER) && (USBD_USE_CDC || USBD_USE_CDC_MSC)
+
+#include "usb_serial.h"
+#include "../../feature/e_parser.h"
+
+EmergencyParser::State emergency_state = EmergencyParser::State::EP_RESET;
+
+int8_t (*USBD_CDC_Receive_original) (uint8_t *Buf, uint32_t *Len) = nullptr;
+
+static int8_t USBD_CDC_Receive_hook(uint8_t *Buf, uint32_t *Len) {
+ for (uint32_t i = 0; i < *Len; i++)
+ emergency_parser.update(emergency_state, Buf[i]);
+ return USBD_CDC_Receive_original(Buf, Len);
+}
+
+typedef struct _USBD_CDC_Itf {
+ int8_t (* Init)(void);
+ int8_t (* DeInit)(void);
+ int8_t (* Control)(uint8_t cmd, uint8_t *pbuf, uint16_t length);
+ int8_t (* Receive)(uint8_t *Buf, uint32_t *Len);
+ int8_t (* Transferred)(void);
+} USBD_CDC_ItfTypeDef;
+
+extern USBD_CDC_ItfTypeDef USBD_CDC_fops;
+
+void USB_Hook_init() {
+ USBD_CDC_Receive_original = USBD_CDC_fops.Receive;
+ USBD_CDC_fops.Receive = USBD_CDC_Receive_hook;
+}
+
+#endif // EMERGENCY_PARSER && USBD_USE_CDC
+#endif // HAL_STM32
diff --git a/Marlin/src/HAL/STM32/usb_serial.h b/Marlin/src/HAL/STM32/usb_serial.h
new file mode 100644
index 0000000000..3edb6fd618
--- /dev/null
+++ b/Marlin/src/HAL/STM32/usb_serial.h
@@ -0,0 +1,24 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+void USB_Hook_init();
diff --git a/Marlin/src/HAL/STM32F1/HAL.cpp b/Marlin/src/HAL/STM32F1/HAL.cpp
new file mode 100644
index 0000000000..4d3140001e
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/HAL.cpp
@@ -0,0 +1,387 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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 .
+ *
+ */
+
+/**
+ * HAL for stm32duino.com based on Libmaple and compatible (STM32F1)
+ */
+
+#ifdef __STM32F1__
+
+#include "../../inc/MarlinConfig.h"
+#include "HAL.h"
+
+#include
+
+// ------------------------
+// Types
+// ------------------------
+
+#define __I
+#define __IO volatile
+ typedef struct {
+ __I uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */
+ __IO uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */
+ __IO uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */
+ __IO uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */
+ __IO uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */
+ __IO uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */
+ __IO uint8_t SHP[12]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */
+ __IO uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */
+ __IO uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */
+ __IO uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */
+ __IO uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */
+ __IO uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */
+ __IO uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */
+ __IO uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */
+ __I uint32_t PFR[2]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */
+ __I uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */
+ __I uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */
+ __I uint32_t MMFR[4]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */
+ __I uint32_t ISAR[5]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */
+ uint32_t RESERVED0[5];
+ __IO uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */
+ } SCB_Type;
+
+// ------------------------
+// Local defines
+// ------------------------
+
+#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */
+#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */
+
+#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */
+
+/* SCB Application Interrupt and Reset Control Register Definitions */
+#define SCB_AIRCR_VECTKEY_Pos 16 /*!< SCB AIRCR: VECTKEY Position */
+#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */
+
+#define SCB_AIRCR_PRIGROUP_Pos 8 /*!< SCB AIRCR: PRIGROUP Position */
+#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */
+
+// ------------------------
+// Serial ports
+// ------------------------
+
+#if defined(SERIAL_USB) && !HAS_SD_HOST_DRIVE
+
+ USBSerial SerialUSB;
+ DefaultSerial1 MSerial0(true, SerialUSB);
+
+ #if ENABLED(EMERGENCY_PARSER)
+ #include "../libmaple/usb/stm32f1/usb_reg_map.h"
+ #include "libmaple/usb_cdcacm.h"
+ // The original callback is not called (no way to retrieve address).
+ // That callback detects a special STM32 reset sequence: this functionality is not essential
+ // as M997 achieves the same.
+ void my_rx_callback(unsigned int, void*) {
+ // max length of 16 is enough to contain all emergency commands
+ uint8 buf[16];
+
+ //rx is usbSerialPart.endpoints[2]
+ uint16 len = usb_get_ep_rx_count(USB_CDCACM_RX_ENDP);
+ uint32 total = usb_cdcacm_data_available();
+
+ if (len == 0 || total == 0 || !WITHIN(total, len, COUNT(buf)))
+ return;
+
+ // cannot get character by character due to bug in composite_cdcacm_peek_ex
+ len = usb_cdcacm_peek(buf, total);
+
+ for (uint32 i = 0; i < len; i++)
+ emergency_parser.update(MSerial0.emergency_state, buf[i + total - len]);
+ }
+ #endif
+#endif
+
+// ------------------------
+// Watchdog Timer
+// ------------------------
+
+#if ENABLED(USE_WATCHDOG)
+
+ #include
+
+ void watchdogSetup() {
+ // do whatever. don't remove this function.
+ }
+
+ /**
+ * The watchdog clock is 40Khz. So for a 4s or 8s interval use a /256 preescaler and 625 or 1250 reload value (counts down to 0).
+ */
+ #define STM32F1_WD_RELOAD TERN(WATCHDOG_DURATION_8S, 1250, 625) // 4 or 8 second timeout
+
+ /**
+ * @brief Initialize the independent hardware watchdog.
+ *
+ * @return No return
+ *
+ * @details The watchdog clock is 40Khz. So for a 4s or 8s interval use a /256 preescaler and 625 or 1250 reload value (counts down to 0).
+ */
+ void MarlinHAL::watchdog_init() {
+ #if DISABLED(DISABLE_WATCHDOG_INIT)
+ iwdg_init(IWDG_PRE_256, STM32F1_WD_RELOAD);
+ #endif
+ }
+
+ // Reset watchdog. MUST be called every 4 or 8 seconds after the
+ // first watchdog_init or the STM32F1 will reset.
+ void MarlinHAL::watchdog_refresh() {
+ #if DISABLED(PINS_DEBUGGING) && PIN_EXISTS(LED)
+ TOGGLE(LED_PIN); // heartbeat indicator
+ #endif
+ iwdg_feed();
+ }
+
+#endif // USE_WATCHDOG
+
+// ------------------------
+// ADC
+// ------------------------
+
+// Watch out for recursion here! Our pin_t is signed, so pass through to Arduino -> analogRead(uint8_t)
+
+uint16_t analogRead(const pin_t pin) {
+ const bool is_analog = _GET_MODE(pin) == GPIO_INPUT_ANALOG;
+ return is_analog ? analogRead(uint8_t(pin)) : 0;
+}
+
+// Wrapper to maple unprotected analogWrite
+void analogWrite(const pin_t pin, int pwm_val8) {
+ if (PWM_PIN(pin)) analogWrite(uint8_t(pin), pwm_val8);
+}
+
+uint16_t MarlinHAL::adc_result;
+
+// ------------------------
+// Private functions
+// ------------------------
+
+static void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) {
+ uint32_t reg_value;
+ uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07); // only values 0..7 are used
+
+ reg_value = SCB->AIRCR; // read old register configuration
+ reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); // clear bits to change
+ reg_value = (reg_value |
+ ((uint32_t)0x5FA << SCB_AIRCR_VECTKEY_Pos) |
+ (PriorityGroupTmp << 8)); // Insert write key & priority group
+ SCB->AIRCR = reg_value;
+}
+
+// ------------------------
+// Public functions
+// ------------------------
+
+void flashFirmware(const int16_t) { hal.reboot(); }
+
+//
+// Leave PA11/PA12 intact if USBSerial is not used
+//
+#if SERIAL_USB
+ namespace wirish { namespace priv {
+ #if SERIAL_PORT > 0
+ #if SERIAL_PORT2
+ #if SERIAL_PORT2 > 0
+ void board_setup_usb() {}
+ #endif
+ #else
+ void board_setup_usb() {}
+ #endif
+ #endif
+ } }
+#endif
+
+TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial());
+
+// ------------------------
+// MarlinHAL class
+// ------------------------
+
+void MarlinHAL::init() {
+ NVIC_SetPriorityGrouping(0x3);
+ #if PIN_EXISTS(LED)
+ OUT_WRITE(LED_PIN, LOW);
+ #endif
+ #if HAS_SD_HOST_DRIVE
+ MSC_SD_init();
+ #elif BOTH(SERIAL_USB, EMERGENCY_PARSER)
+ usb_cdcacm_set_hooks(USB_CDCACM_HOOK_RX, my_rx_callback);
+ #endif
+ #if PIN_EXISTS(USB_CONNECT)
+ OUT_WRITE(USB_CONNECT_PIN, !USB_CONNECT_INVERTING); // USB clear connection
+ delay(1000); // Give OS time to notice
+ WRITE(USB_CONNECT_PIN, USB_CONNECT_INVERTING);
+ #endif
+ TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); // Install the minimal serial handler
+}
+
+// HAL idle task
+void MarlinHAL::idletask() {
+ #if HAS_SHARED_MEDIA
+ // If Marlin is using the SD card we need to lock it to prevent access from
+ // a PC via USB.
+ // Other HALs use IS_SD_PRINTING() and IS_SD_FILE_OPEN() to check for access but
+ // this will not reliably detect delete operations. To be safe we will lock
+ // the disk if Marlin has it mounted. Unfortunately there is currently no way
+ // to unmount the disk from the LCD menu.
+ // if (IS_SD_PRINTING() || IS_SD_FILE_OPEN())
+ /* copy from lpc1768 framework, should be fixed later for process HAS_SD_HOST_DRIVE*/
+ // process USB mass storage device class loop
+ MarlinMSC.loop();
+ #endif
+}
+
+void MarlinHAL::reboot() { nvic_sys_reset(); }
+
+// ------------------------
+// Free Memory Accessor
+// ------------------------
+
+extern "C" {
+ extern unsigned int _ebss; // end of bss section
+}
+
+/**
+ * TODO: Change this to correct it for libmaple
+ */
+
+// return free memory between end of heap (or end bss) and whatever is current
+
+/*
+#include
+//extern caddr_t _sbrk(int incr);
+#ifndef CONFIG_HEAP_END
+extern char _lm_heap_end;
+#define CONFIG_HEAP_END ((caddr_t)&_lm_heap_end)
+#endif
+
+extern "C" {
+ static int freeMemory() {
+ char top = 't';
+ return &top - reinterpret_cast(sbrk(0));
+ }
+ int freeMemory() {
+ int free_memory;
+ int heap_end = (int)_sbrk(0);
+ free_memory = ((int)&free_memory) - ((int)heap_end);
+ return free_memory;
+ }
+}
+*/
+
+// ------------------------
+// ADC
+// ------------------------
+
+enum ADCIndex : uint8_t {
+ OPTITEM(HAS_TEMP_ADC_0, TEMP_0)
+ OPTITEM(HAS_TEMP_ADC_1, TEMP_1)
+ OPTITEM(HAS_TEMP_ADC_2, TEMP_2)
+ OPTITEM(HAS_TEMP_ADC_3, TEMP_3)
+ OPTITEM(HAS_TEMP_ADC_4, TEMP_4)
+ OPTITEM(HAS_TEMP_ADC_5, TEMP_5)
+ OPTITEM(HAS_TEMP_ADC_6, TEMP_6)
+ OPTITEM(HAS_TEMP_ADC_7, TEMP_7)
+ OPTITEM(HAS_HEATED_BED, TEMP_BED)
+ OPTITEM(HAS_TEMP_CHAMBER, TEMP_CHAMBER)
+ OPTITEM(HAS_TEMP_ADC_PROBE, TEMP_PROBE)
+ OPTITEM(HAS_TEMP_COOLER, TEMP_COOLER)
+ OPTITEM(HAS_TEMP_BOARD, TEMP_BOARD)
+ OPTITEM(FILAMENT_WIDTH_SENSOR, FILWIDTH)
+ OPTITEM(HAS_ADC_BUTTONS, ADC_KEY)
+ OPTITEM(HAS_JOY_ADC_X, JOY_X)
+ OPTITEM(HAS_JOY_ADC_Y, JOY_Y)
+ OPTITEM(HAS_JOY_ADC_Z, JOY_Z)
+ OPTITEM(POWER_MONITOR_CURRENT, POWERMON_CURRENT)
+ OPTITEM(POWER_MONITOR_VOLTAGE, POWERMON_VOLTS)
+ ADC_COUNT
+};
+
+static uint16_t adc_results[ADC_COUNT];
+
+// Init the AD in continuous capture mode
+void MarlinHAL::adc_init() {
+ static const uint8_t adc_pins[] = {
+ OPTITEM(HAS_TEMP_ADC_0, TEMP_0_PIN)
+ OPTITEM(HAS_TEMP_ADC_1, TEMP_1_PIN)
+ OPTITEM(HAS_TEMP_ADC_2, TEMP_2_PIN)
+ OPTITEM(HAS_TEMP_ADC_3, TEMP_3_PIN)
+ OPTITEM(HAS_TEMP_ADC_4, TEMP_4_PIN)
+ OPTITEM(HAS_TEMP_ADC_5, TEMP_5_PIN)
+ OPTITEM(HAS_TEMP_ADC_6, TEMP_6_PIN)
+ OPTITEM(HAS_TEMP_ADC_7, TEMP_7_PIN)
+ OPTITEM(HAS_HEATED_BED, TEMP_BED_PIN)
+ OPTITEM(HAS_TEMP_CHAMBER, TEMP_CHAMBER_PIN)
+ OPTITEM(HAS_TEMP_ADC_PROBE, TEMP_PROBE_PIN)
+ OPTITEM(HAS_TEMP_COOLER, TEMP_COOLER_PIN)
+ OPTITEM(HAS_TEMP_BOARD, TEMP_BOARD_PIN)
+ OPTITEM(FILAMENT_WIDTH_SENSOR, FILWIDTH_PIN)
+ OPTITEM(HAS_ADC_BUTTONS, ADC_KEYPAD_PIN)
+ OPTITEM(HAS_JOY_ADC_X, JOY_X_PIN)
+ OPTITEM(HAS_JOY_ADC_Y, JOY_Y_PIN)
+ OPTITEM(HAS_JOY_ADC_Z, JOY_Z_PIN)
+ OPTITEM(POWER_MONITOR_CURRENT, POWER_MONITOR_CURRENT_PIN)
+ OPTITEM(POWER_MONITOR_VOLTAGE, POWER_MONITOR_VOLTAGE_PIN)
+ };
+ static STM32ADC adc(ADC1);
+ // configure the ADC
+ adc.calibrate();
+ adc.setSampleRate((F_CPU > 72000000) ? ADC_SMPR_71_5 : ADC_SMPR_41_5); // 71.5 or 41.5 ADC cycles
+ adc.setPins((uint8_t *)adc_pins, ADC_COUNT);
+ adc.setDMA(adc_results, uint16_t(ADC_COUNT), uint32_t(DMA_MINC_MODE | DMA_CIRC_MODE), nullptr);
+ adc.setScanMode();
+ adc.setContinuous();
+ adc.startConversion();
+}
+
+void MarlinHAL::adc_start(const pin_t pin) {
+ #define __TCASE(N,I) case N: pin_index = I; break;
+ #define _TCASE(C,N,I) TERN_(C, __TCASE(N, I))
+ ADCIndex pin_index;
+ switch (pin) {
+ default: return;
+ _TCASE(HAS_TEMP_ADC_0, TEMP_0_PIN, TEMP_0)
+ _TCASE(HAS_TEMP_ADC_1, TEMP_1_PIN, TEMP_1)
+ _TCASE(HAS_TEMP_ADC_2, TEMP_2_PIN, TEMP_2)
+ _TCASE(HAS_TEMP_ADC_3, TEMP_3_PIN, TEMP_3)
+ _TCASE(HAS_TEMP_ADC_4, TEMP_4_PIN, TEMP_4)
+ _TCASE(HAS_TEMP_ADC_5, TEMP_5_PIN, TEMP_5)
+ _TCASE(HAS_TEMP_ADC_6, TEMP_6_PIN, TEMP_6)
+ _TCASE(HAS_TEMP_ADC_7, TEMP_7_PIN, TEMP_7)
+ _TCASE(HAS_HEATED_BED, TEMP_BED_PIN, TEMP_BED)
+ _TCASE(HAS_TEMP_CHAMBER, TEMP_CHAMBER_PIN, TEMP_CHAMBER)
+ _TCASE(HAS_TEMP_ADC_PROBE, TEMP_PROBE_PIN, TEMP_PROBE)
+ _TCASE(HAS_TEMP_COOLER, TEMP_COOLER_PIN, TEMP_COOLER)
+ _TCASE(HAS_TEMP_BOARD, TEMP_BOARD_PIN, TEMP_BOARD)
+ _TCASE(HAS_JOY_ADC_X, JOY_X_PIN, JOY_X)
+ _TCASE(HAS_JOY_ADC_Y, JOY_Y_PIN, JOY_Y)
+ _TCASE(HAS_JOY_ADC_Z, JOY_Z_PIN, JOY_Z)
+ _TCASE(FILAMENT_WIDTH_SENSOR, FILWIDTH_PIN, FILWIDTH)
+ _TCASE(HAS_ADC_BUTTONS, ADC_KEYPAD_PIN, ADC_KEY)
+ _TCASE(POWER_MONITOR_CURRENT, POWER_MONITOR_CURRENT_PIN, POWERMON_CURRENT)
+ _TCASE(POWER_MONITOR_VOLTAGE, POWER_MONITOR_VOLTAGE_PIN, POWERMON_VOLTS)
+ }
+ adc_result = (adc_results[(int)pin_index] & 0xFFF) >> (12 - HAL_ADC_RESOLUTION); // shift out unused bits
+}
+
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/HAL.h b/Marlin/src/HAL/STM32F1/HAL.h
new file mode 100644
index 0000000000..b14b5f7e79
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/HAL.h
@@ -0,0 +1,309 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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
+
+/**
+ * HAL for stm32duino.com based on Libmaple and compatible (STM32F1)
+ */
+
+#define CPU_32_BIT
+
+#include "../../core/macros.h"
+#include "../shared/Marduino.h"
+#include "../shared/math_32bit.h"
+#include "../shared/HAL_SPI.h"
+
+#include "fastio.h"
+
+#include
+#include
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if HAS_SD_HOST_DRIVE
+ #include "msc_sd.h"
+#endif
+
+#include "MarlinSerial.h"
+
+// ------------------------
+// Defines
+// ------------------------
+
+//
+// Default graphical display delays
+//
+#define CPU_ST7920_DELAY_1 300
+#define CPU_ST7920_DELAY_2 40
+#define CPU_ST7920_DELAY_3 340
+
+#ifndef STM32_FLASH_SIZE
+ #if ANY(MCU_STM32F103RE, MCU_STM32F103VE, MCU_STM32F103ZE)
+ #define STM32_FLASH_SIZE 512
+ #else
+ #define STM32_FLASH_SIZE 256
+ #endif
+#endif
+
+// ------------------------
+// Serial ports
+// ------------------------
+
+#ifdef SERIAL_USB
+ typedef ForwardSerial1Class< USBSerial > DefaultSerial1;
+ extern DefaultSerial1 MSerial0;
+ #if HAS_SD_HOST_DRIVE
+ #define UsbSerial MarlinCompositeSerial
+ #else
+ #define UsbSerial MSerial0
+ #endif
+#endif
+
+#define _MSERIAL(X) MSerial##X
+#define MSERIAL(X) _MSERIAL(X)
+
+#if EITHER(STM32_HIGH_DENSITY, STM32_XL_DENSITY)
+ #define NUM_UARTS 5
+#else
+ #define NUM_UARTS 3
+#endif
+
+#if SERIAL_PORT == -1
+ #define MYSERIAL1 UsbSerial
+#elif WITHIN(SERIAL_PORT, 1, NUM_UARTS)
+ #define MYSERIAL1 MSERIAL(SERIAL_PORT)
+#else
+ #define MYSERIAL1 MSERIAL(1) // dummy port
+ static_assert(false, "SERIAL_PORT must be from 1 to " STRINGIFY(NUM_UARTS) ". You can also use -1 if the board supports Native USB.")
+#endif
+
+#ifdef SERIAL_PORT_2
+ #if SERIAL_PORT_2 == -1
+ #define MYSERIAL2 UsbSerial
+ #elif WITHIN(SERIAL_PORT_2, 1, NUM_UARTS)
+ #define MYSERIAL2 MSERIAL(SERIAL_PORT_2)
+ #else
+ #define MYSERIAL2 MSERIAL(1) // dummy port
+ static_assert(false, "SERIAL_PORT_2 must be from 1 to " STRINGIFY(NUM_UARTS) ". You can also use -1 if the board supports Native USB.")
+ #endif
+#endif
+
+#ifdef SERIAL_PORT_3
+ #if SERIAL_PORT_3 == -1
+ #define MYSERIAL3 UsbSerial
+ #elif WITHIN(SERIAL_PORT_3, 1, NUM_UARTS)
+ #define MYSERIAL3 MSERIAL(SERIAL_PORT_3)
+ #else
+ #define MYSERIAL3 MSERIAL(1) // dummy port
+ static_assert(false, "SERIAL_PORT_3 must be from 1 to " STRINGIFY(NUM_UARTS) ". You can also use -1 if the board supports Native USB.")
+ #endif
+#endif
+
+#ifdef MMU2_SERIAL_PORT
+ #if MMU2_SERIAL_PORT == -1
+ #define MMU2_SERIAL UsbSerial
+ #elif WITHIN(MMU2_SERIAL_PORT, 1, NUM_UARTS)
+ #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT)
+ #else
+ #define MMU2_SERIAL MSERIAL(1) // dummy port
+ static_assert(false, "MMU2_SERIAL_PORT must be from 1 to " STRINGIFY(NUM_UARTS) ". You can also use -1 if the board supports Native USB.")
+ #endif
+#endif
+
+#ifdef LCD_SERIAL_PORT
+ #if LCD_SERIAL_PORT == -1
+ #define LCD_SERIAL UsbSerial
+ #elif WITHIN(LCD_SERIAL_PORT, 1, NUM_UARTS)
+ #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT)
+ #else
+ #define LCD_SERIAL MSERIAL(1) // dummy port
+ static_assert(false, "LCD_SERIAL_PORT must be from 1 to " STRINGIFY(NUM_UARTS) ". You can also use -1 if the board supports Native USB.")
+ #endif
+ #if HAS_DGUS_LCD
+ #define SERIAL_GET_TX_BUFFER_FREE() LCD_SERIAL.availableForWrite()
+ #endif
+#endif
+
+/**
+ * TODO: review this to return 1 for pins that are not analog input
+ */
+#ifndef analogInputToDigitalPin
+ #define analogInputToDigitalPin(p) (p)
+#endif
+
+#ifndef digitalPinHasPWM
+ #define digitalPinHasPWM(P) !!PIN_MAP[P].timer_device
+ #define NO_COMPILE_TIME_PWM
+#endif
+
+// Reset Reason
+#define RST_POWER_ON 1
+#define RST_EXTERNAL 2
+#define RST_BROWN_OUT 4
+#define RST_WATCHDOG 8
+#define RST_JTAG 16
+#define RST_SOFTWARE 32
+#define RST_BACKUP 64
+
+// ------------------------
+// Types
+// ------------------------
+
+typedef int8_t pin_t;
+
+// ------------------------
+// Interrupts
+// ------------------------
+
+#define CRITICAL_SECTION_START() const bool irqon = !__get_primask(); (void)__iCliRetVal()
+#define CRITICAL_SECTION_END() if (!irqon) (void)__iSeiRetVal()
+#define cli() noInterrupts()
+#define sei() interrupts()
+
+// ------------------------
+// ADC
+// ------------------------
+
+#ifdef ADC_RESOLUTION
+ #define HAL_ADC_RESOLUTION ADC_RESOLUTION
+#else
+ #define HAL_ADC_RESOLUTION 12
+#endif
+
+#define HAL_ADC_VREF 3.3
+
+uint16_t analogRead(const pin_t pin); // need hal.adc_enable() first
+void analogWrite(const pin_t pin, int pwm_val8); // PWM only! mul by 257 in maple!?
+
+//
+// Pin Mapping for M42, M43, M226
+//
+#define GET_PIN_MAP_PIN(index) index
+#define GET_PIN_MAP_INDEX(pin) pin
+#define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval)
+
+#define JTAG_DISABLE() afio_cfg_debug_ports(AFIO_DEBUG_SW_ONLY)
+#define JTAGSWD_DISABLE() afio_cfg_debug_ports(AFIO_DEBUG_NONE)
+
+#define PLATFORM_M997_SUPPORT
+void flashFirmware(const int16_t);
+
+#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment
+#ifndef PWM_FREQUENCY
+ #define PWM_FREQUENCY 1000 // Default PWM Frequency
+#endif
+
+// ------------------------
+// Class Utilities
+// ------------------------
+
+// Memory related
+#define __bss_end __bss_end__
+
+void _delay_ms(const int ms);
+
+extern "C" char* _sbrk(int incr);
+
+#pragma GCC diagnostic push
+#if GCC_VERSION <= 50000
+ #pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+static inline int freeMemory() {
+ volatile char top;
+ return &top - _sbrk(0);
+}
+
+#pragma GCC diagnostic pop
+
+// ------------------------
+// MarlinHAL Class
+// ------------------------
+
+class MarlinHAL {
+public:
+
+ // Earliest possible init, before setup()
+ MarlinHAL() {}
+
+ // Watchdog
+ static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {});
+ static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {});
+
+ static void init(); // Called early in setup()
+ static void init_board() {} // Called less early in setup()
+ static void reboot(); // Restart the firmware from 0x0
+
+ // Interrupts
+ static bool isr_state() { return !__get_primask(); }
+ static void isr_on() { ((void)__iSeiRetVal()); }
+ static void isr_off() { ((void)__iCliRetVal()); }
+
+ static void delay_ms(const int ms) { delay(ms); }
+
+ // Tasks, called from idle()
+ static void idletask();
+
+ // Reset
+ static uint8_t get_reset_source() { return RST_POWER_ON; }
+ static void clear_reset_source() {}
+
+ // Free SRAM
+ static int freeMemory() { return ::freeMemory(); }
+
+ //
+ // ADC Methods
+ //
+
+ static uint16_t adc_result;
+
+ // Called by Temperature::init once at startup
+ static void adc_init();
+
+ // Called by Temperature::init for each sensor at startup
+ static void adc_enable(const pin_t pin) { pinMode(pin, INPUT_ANALOG); }
+
+ // Begin ADC sampling on the given pin. Called from Temperature::isr!
+ static void adc_start(const pin_t pin);
+
+ // Is the ADC ready for reading?
+ static bool adc_ready() { return true; }
+
+ // The current value of the ADC register
+ static uint16_t adc_value() { return adc_result; }
+
+ /**
+ * Set the PWM duty cycle for the pin to the given value.
+ * Optionally invert the duty cycle [default = false]
+ * Optionally change the maximum size of the provided value to enable finer PWM duty control [default = 255]
+ * The timer must be pre-configured with set_pwm_frequency() if the default frequency is not desired.
+ */
+ static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false);
+
+ /**
+ * Set the frequency of the timer for the given pin.
+ * All Timer PWM pins run at the same frequency.
+ */
+ static void set_pwm_frequency(const pin_t pin, const uint16_t f_desired);
+
+};
diff --git a/Marlin/src/HAL/STM32F1/HAL_SPI.cpp b/Marlin/src/HAL/STM32F1/HAL_SPI.cpp
new file mode 100644
index 0000000000..abb348d743
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/HAL_SPI.cpp
@@ -0,0 +1,171 @@
+/**
+ * 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
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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 .
+ *
+ */
+
+/**
+ * Software SPI functions originally from Arduino Sd2Card Library
+ * Copyright (c) 2009 by William Greiman
+ * Adapted to the STM32F1 HAL
+ */
+
+#ifdef __STM32F1__
+
+#include "../../inc/MarlinConfig.h"
+#include
+
+// ------------------------
+// Public functions
+// ------------------------
+
+#if ENABLED(SOFTWARE_SPI)
+
+ // ------------------------
+ // Software SPI
+ // ------------------------
+ #error "Software SPI not supported for STM32F1. Use hardware SPI."
+
+#else
+
+// ------------------------
+// Hardware SPI
+// ------------------------
+
+/**
+ * VGPV SPI speed start and F_CPU/2, by default 72/2 = 36Mhz
+ */
+
+/**
+ * @brief Begin SPI port setup
+ *
+ * @return Nothing
+ *
+ * @details Only configures SS pin since libmaple creates and initialize the SPI object
+ */
+void spiBegin() {
+ #if PIN_EXISTS(SD_SS)
+ OUT_WRITE(SD_SS_PIN, HIGH);
+ #endif
+}
+
+/**
+ * @brief Initialize SPI port to required speed rate and transfer mode (MSB, SPI MODE 0)
+ *
+ * @param spiRate Rate as declared in HAL.h (speed do not match AVR)
+ * @return Nothing
+ *
+ * @details
+ */
+void spiInit(uint8_t spiRate) {
+ /**
+ * STM32F1 APB2 = 72MHz, APB1 = 36MHz, max SPI speed of this MCU if 18Mhz
+ * STM32F1 has 3 SPI ports, SPI1 in APB2, SPI2/SPI3 in APB1
+ * so the minimum prescale of SPI1 is DIV4, SPI2/SPI3 is DIV2
+ */
+ #if SPI_DEVICE == 1
+ #define SPI_CLOCK_MAX SPI_CLOCK_DIV4
+ #else
+ #define SPI_CLOCK_MAX SPI_CLOCK_DIV2
+ #endif
+ uint8_t clock;
+ switch (spiRate) {
+ case SPI_FULL_SPEED: clock = SPI_CLOCK_MAX ; break;
+ case SPI_HALF_SPEED: clock = SPI_CLOCK_DIV4 ; break;
+ case SPI_QUARTER_SPEED: clock = SPI_CLOCK_DIV8 ; break;
+ case SPI_EIGHTH_SPEED: clock = SPI_CLOCK_DIV16; break;
+ case SPI_SPEED_5: clock = SPI_CLOCK_DIV32; break;
+ case SPI_SPEED_6: clock = SPI_CLOCK_DIV64; break;
+ default: clock = SPI_CLOCK_DIV2; // Default from the SPI library
+ }
+ SPI.setModule(SPI_DEVICE);
+ SPI.begin();
+ SPI.setClockDivider(clock);
+ SPI.setBitOrder(MSBFIRST);
+ SPI.setDataMode(SPI_MODE0);
+}
+
+/**
+ * @brief Receive a single byte from the SPI port.
+ *
+ * @return Byte received
+ *
+ * @details
+ */
+uint8_t spiRec() {
+ uint8_t returnByte = SPI.transfer(0xFF);
+ return returnByte;
+}
+
+/**
+ * @brief Receive a number of bytes from the SPI port to a buffer
+ *
+ * @param buf Pointer to starting address of buffer to write to.
+ * @param nbyte Number of bytes to receive.
+ * @return Nothing
+ *
+ * @details Uses DMA
+ */
+void spiRead(uint8_t *buf, uint16_t nbyte) {
+ SPI.dmaTransfer(0, const_cast(buf), nbyte);
+}
+
+/**
+ * @brief Send a single byte on SPI port
+ *
+ * @param b Byte to send
+ *
+ * @details
+ */
+void spiSend(uint8_t b) {
+ SPI.send(b);
+}
+
+/**
+ * @brief Write token and then write from 512 byte buffer to SPI (for SD card)
+ *
+ * @param buf Pointer with buffer start address
+ * @return Nothing
+ *
+ * @details Use DMA
+ */
+void spiSendBlock(uint8_t token, const uint8_t *buf) {
+ SPI.send(token);
+ SPI.dmaSend(const_cast(buf), 512);
+}
+
+#if ENABLED(SPI_EEPROM)
+
+// Read single byte from specified SPI channel
+uint8_t spiRec(uint32_t chan) { return SPI.transfer(0xFF); }
+
+// Write single byte to specified SPI channel
+void spiSend(uint32_t chan, byte b) { SPI.send(b); }
+
+// Write buffer to specified SPI channel
+void spiSend(uint32_t chan, const uint8_t *buf, size_t n) {
+ for (size_t p = 0; p < n; p++) spiSend(chan, buf[p]);
+}
+
+#endif // SPI_EEPROM
+
+#endif // SOFTWARE_SPI
+
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/MarlinSPI.h b/Marlin/src/HAL/STM32F1/MarlinSPI.h
new file mode 100644
index 0000000000..fab245f904
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/MarlinSPI.h
@@ -0,0 +1,45 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include
+
+/**
+ * Marlin currently requires 3 SPI classes:
+ *
+ * SPIClass:
+ * This class is normally provided by frameworks and has a semi-default interface.
+ * This is needed because some libraries reference it globally.
+ *
+ * SPISettings:
+ * Container for SPI configs for SPIClass. As above, libraries may reference it globally.
+ *
+ * These two classes are often provided by frameworks so we cannot extend them to add
+ * useful methods for Marlin.
+ *
+ * MarlinSPI:
+ * Provides the default SPIClass interface plus some Marlin goodies such as a simplified
+ * interface for SPI DMA transfer.
+ *
+ */
+
+using MarlinSPI = SPIClass;
diff --git a/Marlin/src/HAL/STM32F1/MarlinSerial.cpp b/Marlin/src/HAL/STM32F1/MarlinSerial.cpp
new file mode 100644
index 0000000000..6dabcde51e
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/MarlinSerial.cpp
@@ -0,0 +1,204 @@
+/**
+ * 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 .
+ *
+ */
+
+#ifdef __STM32F1__
+
+#include "../../inc/MarlinConfig.h"
+#include "MarlinSerial.h"
+#include
+
+// Copied from ~/.platformio/packages/framework-arduinoststm32-maple/STM32F1/system/libmaple/usart_private.h
+// Changed to handle Emergency Parser
+static inline __always_inline void my_usart_irq(ring_buffer *rb, ring_buffer *wb, usart_reg_map *regs, MSerialT &serial) {
+ /* Handle RXNEIE and TXEIE interrupts.
+ * RXNE signifies availability of a byte in DR.
+ *
+ * See table 198 (sec 27.4, p809) in STM document RM0008 rev 15.
+ * We enable RXNEIE.
+ */
+ uint32_t srflags = regs->SR, cr1its = regs->CR1;
+
+ if ((cr1its & USART_CR1_RXNEIE) && (srflags & USART_SR_RXNE)) {
+ if (srflags & USART_SR_FE || srflags & USART_SR_PE ) {
+ // framing error or parity error
+ regs->DR; // Read and throw away the data, which also clears FE and PE
+ }
+ else {
+ uint8_t c = (uint8)regs->DR;
+ #ifdef USART_SAFE_INSERT
+ // If the buffer is full and the user defines USART_SAFE_INSERT,
+ // ignore new bytes.
+ rb_safe_insert(rb, c);
+ #else
+ // By default, push bytes around in the ring buffer.
+ rb_push_insert(rb, c);
+ #endif
+ #if ENABLED(EMERGENCY_PARSER)
+ if (serial.emergency_parser_enabled())
+ emergency_parser.update(serial.emergency_state, c);
+ #endif
+ }
+ }
+ else if (srflags & USART_SR_ORE) {
+ // overrun and empty data, just do a dummy read to clear ORE
+ // and prevent a raise condition where a continuous interrupt stream (due to ORE set) occurs
+ // (see chapter "Overrun error" ) in STM32 reference manual
+ regs->DR;
+ }
+
+ // TXE signifies readiness to send a byte to DR.
+ if ((cr1its & USART_CR1_TXEIE) && (srflags & USART_SR_TXE)) {
+ if (!rb_is_empty(wb))
+ regs->DR=rb_remove(wb);
+ else
+ regs->CR1 &= ~((uint32)USART_CR1_TXEIE); // disable TXEIE
+ }
+}
+
+// Not every MarlinSerial port should handle emergency parsing.
+// It would not make sense to parse GCode from TMC responses, for example.
+constexpr bool serial_handles_emergency(int port) {
+ return false
+ #ifdef SERIAL_PORT
+ || (SERIAL_PORT) == port
+ #endif
+ #ifdef SERIAL_PORT_2
+ || (SERIAL_PORT_2) == port
+ #endif
+ #ifdef LCD_SERIAL_PORT
+ || (LCD_SERIAL_PORT) == port
+ #endif
+ ;
+}
+
+#define DEFINE_HWSERIAL_MARLIN(name, n) \
+ MSerialT name(serial_handles_emergency(n),\
+ USART##n, \
+ BOARD_USART##n##_TX_PIN, \
+ BOARD_USART##n##_RX_PIN); \
+ extern "C" void __irq_usart##n(void) { \
+ my_usart_irq(USART##n->rb, USART##n->wb, USART##n##_BASE, MSerial##n); \
+ }
+
+#define DEFINE_HWSERIAL_UART_MARLIN(name, n) \
+ MSerialT name(serial_handles_emergency(n), \
+ UART##n, \
+ BOARD_USART##n##_TX_PIN, \
+ BOARD_USART##n##_RX_PIN); \
+ extern "C" void __irq_usart##n(void) { \
+ my_usart_irq(UART##n->rb, UART##n->wb, UART##n##_BASE, MSerial##n); \
+ }
+
+// Instantiate all UARTs even if they are not needed
+// This avoids a bunch of logic to figure out every serial
+// port which may be in use on the system.
+#if DISABLED(MKS_WIFI_MODULE)
+ DEFINE_HWSERIAL_MARLIN(MSerial1, 1);
+#endif
+DEFINE_HWSERIAL_MARLIN(MSerial2, 2);
+DEFINE_HWSERIAL_MARLIN(MSerial3, 3);
+#if EITHER(STM32_HIGH_DENSITY, STM32_XL_DENSITY)
+ DEFINE_HWSERIAL_UART_MARLIN(MSerial4, 4);
+ DEFINE_HWSERIAL_UART_MARLIN(MSerial5, 5);
+#endif
+
+// Check the type of each serial port by passing it to a template function.
+// HardwareSerial is known to sometimes hang the controller when an error occurs,
+// so this case will fail the static assert. All other classes are assumed to be ok.
+template
+constexpr bool IsSerialClassAllowed(const T&) { return true; }
+constexpr bool IsSerialClassAllowed(const HardwareSerial&) { return false; }
+
+#define CHECK_CFG_SERIAL(A) static_assert(IsSerialClassAllowed(A), STRINGIFY(A) " is defined incorrectly");
+#define CHECK_AXIS_SERIAL(A) static_assert(IsSerialClassAllowed(A##_HARDWARE_SERIAL), STRINGIFY(A) "_HARDWARE_SERIAL must be defined in the form MSerial1, rather than Serial1");
+
+// If you encounter this error, replace SerialX with MSerialX, for example MSerial3.
+
+// Non-TMC ports were already validated in HAL.h, so do not require verbose error messages.
+#ifdef MYSERIAL1
+ CHECK_CFG_SERIAL(MYSERIAL1);
+#endif
+#ifdef MYSERIAL2
+ CHECK_CFG_SERIAL(MYSERIAL2);
+#endif
+#ifdef LCD_SERIAL
+ CHECK_CFG_SERIAL(LCD_SERIAL);
+#endif
+#if AXIS_HAS_HW_SERIAL(X)
+ CHECK_AXIS_SERIAL(X);
+#endif
+#if AXIS_HAS_HW_SERIAL(X2)
+ CHECK_AXIS_SERIAL(X2);
+#endif
+#if AXIS_HAS_HW_SERIAL(Y)
+ CHECK_AXIS_SERIAL(Y);
+#endif
+#if AXIS_HAS_HW_SERIAL(Y2)
+ CHECK_AXIS_SERIAL(Y2);
+#endif
+#if AXIS_HAS_HW_SERIAL(Z)
+ CHECK_AXIS_SERIAL(Z);
+#endif
+#if AXIS_HAS_HW_SERIAL(Z2)
+ CHECK_AXIS_SERIAL(Z2);
+#endif
+#if AXIS_HAS_HW_SERIAL(Z3)
+ CHECK_AXIS_SERIAL(Z3);
+#endif
+#if AXIS_HAS_HW_SERIAL(Z4)
+ CHECK_AXIS_SERIAL(Z4);
+#endif
+#if AXIS_HAS_HW_SERIAL(I)
+ CHECK_AXIS_SERIAL(I);
+#endif
+#if AXIS_HAS_HW_SERIAL(J)
+ CHECK_AXIS_SERIAL(J);
+#endif
+#if AXIS_HAS_HW_SERIAL(K)
+ CHECK_AXIS_SERIAL(K);
+#endif
+#if AXIS_HAS_HW_SERIAL(E0)
+ CHECK_AXIS_SERIAL(E0);
+#endif
+#if AXIS_HAS_HW_SERIAL(E1)
+ CHECK_AXIS_SERIAL(E1);
+#endif
+#if AXIS_HAS_HW_SERIAL(E2)
+ CHECK_AXIS_SERIAL(E2);
+#endif
+#if AXIS_HAS_HW_SERIAL(E3)
+ CHECK_AXIS_SERIAL(E3);
+#endif
+#if AXIS_HAS_HW_SERIAL(E4)
+ CHECK_AXIS_SERIAL(E4);
+#endif
+#if AXIS_HAS_HW_SERIAL(E5)
+ CHECK_AXIS_SERIAL(E5);
+#endif
+#if AXIS_HAS_HW_SERIAL(E6)
+ CHECK_AXIS_SERIAL(E6);
+#endif
+#if AXIS_HAS_HW_SERIAL(E7)
+ CHECK_AXIS_SERIAL(E7);
+#endif
+
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/MarlinSerial.h b/Marlin/src/HAL/STM32F1/MarlinSerial.h
new file mode 100644
index 0000000000..dda32fe7a2
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/MarlinSerial.h
@@ -0,0 +1,58 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include
+#include
+#include
+
+#include "../../inc/MarlinConfigPre.h"
+#include "../../core/serial_hook.h"
+
+// Increase priority of serial interrupts, to reduce overflow errors
+#define UART_IRQ_PRIO 1
+
+struct MarlinSerial : public HardwareSerial {
+ MarlinSerial(struct usart_dev *usart_device, uint8 tx_pin, uint8 rx_pin) : HardwareSerial(usart_device, tx_pin, rx_pin) { }
+
+ #ifdef UART_IRQ_PRIO
+ // Shadow the parent methods to set IRQ priority after begin()
+ void begin(uint32 baud) {
+ MarlinSerial::begin(baud, SERIAL_8N1);
+ }
+
+ void begin(uint32 baud, uint8_t config) {
+ HardwareSerial::begin(baud, config);
+ nvic_irq_set_priority(c_dev()->irq_num, UART_IRQ_PRIO);
+ }
+ #endif
+};
+
+typedef Serial1Class MSerialT;
+
+extern MSerialT MSerial1;
+extern MSerialT MSerial2;
+extern MSerialT MSerial3;
+#if EITHER(STM32_HIGH_DENSITY, STM32_XL_DENSITY)
+ extern MSerialT MSerial4;
+ extern MSerialT MSerial5;
+#endif
diff --git a/Marlin/src/HAL/STM32F1/MinSerial.cpp b/Marlin/src/HAL/STM32F1/MinSerial.cpp
new file mode 100644
index 0000000000..6cf68d8d8f
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/MinSerial.cpp
@@ -0,0 +1,117 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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 .
+ *
+ */
+#ifdef __STM32F1__
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if ENABLED(POSTMORTEM_DEBUGGING)
+
+#include "../shared/MinSerial.h"
+
+#include
+#include
+#include
+
+/* Instruction Synchronization Barrier */
+#define isb() __asm__ __volatile__ ("isb" : : : "memory")
+
+/* Data Synchronization Barrier */
+#define dsb() __asm__ __volatile__ ("dsb" : : : "memory")
+
+static void TXBegin() {
+ #if !WITHIN(SERIAL_PORT, 1, 6)
+ #warning "Using POSTMORTEM_DEBUGGING requires a physical U(S)ART hardware in case of severe error."
+ #warning "Disabling the severe error reporting feature currently because the used serial port is not a HW port."
+ #else
+ // We use MYSERIAL1 here, so we need to figure out how to get the linked register
+ struct usart_dev* dev = MYSERIAL1.c_dev();
+
+ // Or use this if removing libmaple
+ // int irq = dev->irq_num;
+ // int nvicUART[] = { NVIC_USART1 /* = 37 */, NVIC_USART2 /* = 38 */, NVIC_USART3 /* = 39 */, NVIC_UART4 /* = 52 */, NVIC_UART5 /* = 53 */ };
+ // Disabling irq means setting the bit in the NVIC ICER register located at
+ // Disable UART interrupt in NVIC
+ nvic_irq_disable(dev->irq_num);
+
+ // Use this if removing libmaple
+ //SBI(NVIC_BASE->ICER[1], irq - 32);
+
+ // We NEED memory barriers to ensure Interrupts are actually disabled!
+ // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
+ dsb();
+ isb();
+
+ rcc_clk_disable(dev->clk_id);
+ rcc_clk_enable(dev->clk_id);
+
+ usart_reg_map *regs = dev->regs;
+ regs->CR1 = 0; // Reset the USART
+ regs->CR2 = 0; // 1 stop bit
+
+ // If we don't touch the BRR (baudrate register), we don't need to recompute. Else we would need to call
+ usart_set_baud_rate(dev, 0, BAUDRATE);
+
+ regs->CR1 = (USART_CR1_TE | USART_CR1_UE); // 8 bits, no parity, 1 stop bit
+ #endif
+}
+
+// A SW memory barrier, to ensure GCC does not overoptimize loops
+#define sw_barrier() __asm__ volatile("": : :"memory");
+static void TX(char c) {
+ #if WITHIN(SERIAL_PORT, 1, 6)
+ struct usart_dev* dev = MYSERIAL1.c_dev();
+ while (!(dev->regs->SR & USART_SR_TXE)) {
+ hal.watchdog_refresh();
+ sw_barrier();
+ }
+ dev->regs->DR = c;
+ #endif
+}
+
+void install_min_serial() {
+ HAL_min_serial_init = &TXBegin;
+ HAL_min_serial_out = &TX;
+}
+
+#if DISABLED(DYNAMIC_VECTORTABLE) && DISABLED(STM32F0xx) // Cortex M0 can't branch to a symbol that's too far, so we have a specific hack for them
+extern "C" {
+ __attribute__((naked)) void JumpHandler_ASM() {
+ __asm__ __volatile__ (
+ "b CommonHandler_ASM\n"
+ );
+ }
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_hardfault();
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_busfault();
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_usagefault();
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_memmanage();
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_nmi();
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception7();
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception8();
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception9();
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception10();
+ void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception13();
+}
+#endif
+
+#endif // POSTMORTEM_DEBUGGING
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/README.md b/Marlin/src/HAL/STM32F1/README.md
new file mode 100644
index 0000000000..b5bd5141fb
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/README.md
@@ -0,0 +1,11 @@
+# STM32F1
+
+This HAL is for STM32F103 boards used with [Arduino STM32](https://github.com/rogerclarkmelbourne/Arduino_STM32) framework.
+
+Currently has been tested in Malyan M200 (103CBT6), SKRmini (103RCT6), Chitu 3d (103ZET6), and various 103VET6 boards.
+
+### Main developers:
+- Victorpv
+- xC000005
+- thisiskeithb
+- tpruvot
diff --git a/Marlin/src/HAL/STM32F1/SPI.cpp b/Marlin/src/HAL/STM32F1/SPI.cpp
new file mode 100644
index 0000000000..a180684757
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/SPI.cpp
@@ -0,0 +1,736 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2010 Perry Hung.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
+
+/**
+ * @author Marti Bolivar
+ * @brief Wirish SPI implementation.
+ */
+
+#ifdef __STM32F1__
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include "../../inc/MarlinConfig.h"
+#include "spi_pins.h"
+
+/** Time in ms for DMA receive timeout */
+#define DMA_TIMEOUT 100
+
+#if CYCLES_PER_MICROSECOND != 72
+ #warning "Unexpected clock speed; SPI frequency calculation will be incorrect"
+#endif
+
+struct spi_pins { uint8_t nss, sck, miso, mosi; };
+
+static const spi_pins* dev_to_spi_pins(spi_dev *dev);
+static void configure_gpios(spi_dev *dev, bool as_master);
+static spi_baud_rate determine_baud_rate(spi_dev *dev, uint32_t freq);
+
+#if BOARD_NR_SPI >= 3 && !defined(STM32_HIGH_DENSITY)
+ #error "The SPI library is misconfigured: 3 SPI ports only available on high density STM32 devices"
+#endif
+
+static const spi_pins board_spi_pins[] __FLASH__ = {
+ #if BOARD_NR_SPI >= 1
+ { BOARD_SPI1_NSS_PIN,
+ BOARD_SPI1_SCK_PIN,
+ BOARD_SPI1_MISO_PIN,
+ BOARD_SPI1_MOSI_PIN },
+ #endif
+ #if BOARD_NR_SPI >= 2
+ { BOARD_SPI2_NSS_PIN,
+ BOARD_SPI2_SCK_PIN,
+ BOARD_SPI2_MISO_PIN,
+ BOARD_SPI2_MOSI_PIN },
+ #endif
+ #if BOARD_NR_SPI >= 3
+ { BOARD_SPI3_NSS_PIN,
+ BOARD_SPI3_SCK_PIN,
+ BOARD_SPI3_MISO_PIN,
+ BOARD_SPI3_MOSI_PIN },
+ #endif
+};
+
+#if BOARD_NR_SPI >= 1
+ static void *_spi1_this;
+#endif
+#if BOARD_NR_SPI >= 2
+ static void *_spi2_this;
+#endif
+#if BOARD_NR_SPI >= 3
+ static void *_spi3_this;
+#endif
+
+/**
+ * @brief Wait until TXE (tx empty) flag is set and BSY (busy) flag unset.
+ */
+static inline void waitSpiTxEnd(spi_dev *spi_d) {
+ while (spi_is_tx_empty(spi_d) == 0) { /* nada */ } // wait until TXE=1
+ while (spi_is_busy(spi_d) != 0) { /* nada */ } // wait until BSY=0
+}
+
+/**
+ * Constructor
+ */
+SPIClass::SPIClass(uint32_t spi_num) {
+ _currentSetting = &_settings[spi_num - 1]; // SPI channels are called 1 2 and 3 but the array is zero indexed
+
+ switch (spi_num) {
+ #if BOARD_NR_SPI >= 1
+ case 1:
+ _currentSetting->spi_d = SPI1;
+ _spi1_this = (void*)this;
+ break;
+ #endif
+ #if BOARD_NR_SPI >= 2
+ case 2:
+ _currentSetting->spi_d = SPI2;
+ _spi2_this = (void*)this;
+ break;
+ #endif
+ #if BOARD_NR_SPI >= 3
+ case 3:
+ _currentSetting->spi_d = SPI3;
+ _spi3_this = (void*)this;
+ break;
+ #endif
+ default: ASSERT(0);
+ }
+
+ // Init things specific to each SPI device
+ // clock divider setup is a bit of hack, and needs to be improved at a later date.
+ #if BOARD_NR_SPI >= 1
+ _settings[0].spi_d = SPI1;
+ _settings[0].clockDivider = determine_baud_rate(_settings[0].spi_d, _settings[0].clock);
+ _settings[0].spiDmaDev = DMA1;
+ _settings[0].spiTxDmaChannel = DMA_CH3;
+ _settings[0].spiRxDmaChannel = DMA_CH2;
+ #endif
+ #if BOARD_NR_SPI >= 2
+ _settings[1].spi_d = SPI2;
+ _settings[1].clockDivider = determine_baud_rate(_settings[1].spi_d, _settings[1].clock);
+ _settings[1].spiDmaDev = DMA1;
+ _settings[1].spiTxDmaChannel = DMA_CH5;
+ _settings[1].spiRxDmaChannel = DMA_CH4;
+ #endif
+ #if BOARD_NR_SPI >= 3
+ _settings[2].spi_d = SPI3;
+ _settings[2].clockDivider = determine_baud_rate(_settings[2].spi_d, _settings[2].clock);
+ _settings[2].spiDmaDev = DMA2;
+ _settings[2].spiTxDmaChannel = DMA_CH2;
+ _settings[2].spiRxDmaChannel = DMA_CH1;
+ #endif
+
+ // added for DMA callbacks.
+ _currentSetting->state = SPI_STATE_IDLE;
+}
+
+SPIClass::SPIClass(int8_t mosi, int8_t miso, int8_t sclk, int8_t ssel) : SPIClass(1) {
+ #if BOARD_NR_SPI >= 1
+ if (mosi == BOARD_SPI1_MOSI_PIN) setModule(1);
+ #endif
+ #if BOARD_NR_SPI >= 2
+ if (mosi == BOARD_SPI2_MOSI_PIN) setModule(2);
+ #endif
+ #if BOARD_NR_SPI >= 3
+ if (mosi == BOARD_SPI3_MOSI_PIN) setModule(3);
+ #endif
+}
+
+/**
+ * Set up/tear down
+ */
+void SPIClass::updateSettings() {
+ uint32_t flags = ((_currentSetting->bitOrder == MSBFIRST ? SPI_FRAME_MSB : SPI_FRAME_LSB) | _currentSetting->dataSize | SPI_SW_SLAVE | SPI_SOFT_SS);
+ spi_master_enable(_currentSetting->spi_d, (spi_baud_rate)_currentSetting->clockDivider, (spi_mode)_currentSetting->dataMode, flags);
+}
+
+void SPIClass::begin() {
+ spi_init(_currentSetting->spi_d);
+ configure_gpios(_currentSetting->spi_d, 1);
+ updateSettings();
+ // added for DMA callbacks.
+ _currentSetting->state = SPI_STATE_READY;
+}
+
+void SPIClass::beginSlave() {
+ spi_init(_currentSetting->spi_d);
+ configure_gpios(_currentSetting->spi_d, 0);
+ uint32_t flags = ((_currentSetting->bitOrder == MSBFIRST ? SPI_FRAME_MSB : SPI_FRAME_LSB) | _currentSetting->dataSize);
+ spi_slave_enable(_currentSetting->spi_d, (spi_mode)_currentSetting->dataMode, flags);
+ // added for DMA callbacks.
+ _currentSetting->state = SPI_STATE_READY;
+}
+
+void SPIClass::end() {
+ if (!spi_is_enabled(_currentSetting->spi_d)) return;
+
+ // Follows RM0008's sequence for disabling a SPI in master/slave
+ // full duplex mode.
+ while (spi_is_rx_nonempty(_currentSetting->spi_d)) {
+ // FIXME [0.1.0] remove this once you have an interrupt based driver
+ volatile uint16_t rx __attribute__((unused)) = spi_rx_reg(_currentSetting->spi_d);
+ }
+ waitSpiTxEnd(_currentSetting->spi_d);
+
+ spi_peripheral_disable(_currentSetting->spi_d);
+ // added for DMA callbacks.
+ // Need to add unsetting the callbacks for the DMA channels.
+ _currentSetting->state = SPI_STATE_IDLE;
+}
+
+/* Roger Clark added 3 functions */
+void SPIClass::setClockDivider(uint32_t clockDivider) {
+ _currentSetting->clockDivider = clockDivider;
+ uint32_t cr1 = _currentSetting->spi_d->regs->CR1 & ~(SPI_CR1_BR);
+ _currentSetting->spi_d->regs->CR1 = cr1 | (clockDivider & SPI_CR1_BR);
+}
+
+void SPIClass::setBitOrder(BitOrder bitOrder) {
+ _currentSetting->bitOrder = bitOrder;
+ uint32_t cr1 = _currentSetting->spi_d->regs->CR1 & ~(SPI_CR1_LSBFIRST);
+ if (bitOrder == LSBFIRST) cr1 |= SPI_CR1_LSBFIRST;
+ _currentSetting->spi_d->regs->CR1 = cr1;
+}
+
+/**
+ * Victor Perez. Added to test changing datasize from 8 to 16 bit modes on the fly.
+ * Input parameter should be SPI_CR1_DFF set to 0 or 1 on a 32bit word.
+ */
+void SPIClass::setDataSize(uint32_t datasize) {
+ _currentSetting->dataSize = datasize;
+ uint32_t cr1 = _currentSetting->spi_d->regs->CR1 & ~(SPI_CR1_DFF);
+ uint8_t en = spi_is_enabled(_currentSetting->spi_d);
+ spi_peripheral_disable(_currentSetting->spi_d);
+ _currentSetting->spi_d->regs->CR1 = cr1 | (datasize & SPI_CR1_DFF) | en;
+}
+
+void SPIClass::setDataMode(uint8_t dataMode) {
+ /**
+ * Notes:
+ * As far as we know the AVR numbers for dataMode match the numbers required by the STM32.
+ * From the AVR doc https://www.atmel.com/images/doc2585.pdf section 2.4
+ *
+ * SPI Mode CPOL CPHA Shift SCK-edge Capture SCK-edge
+ * 0 0 0 Falling Rising
+ * 1 0 1 Rising Falling
+ * 2 1 0 Rising Falling
+ * 3 1 1 Falling Rising
+ *
+ * On the STM32 it appears to be
+ *
+ * bit 1 - CPOL : Clock polarity
+ * (This bit should not be changed when communication is ongoing)
+ * 0 : CLK to 0 when idle
+ * 1 : CLK to 1 when idle
+ *
+ * bit 0 - CPHA : Clock phase
+ * (This bit should not be changed when communication is ongoing)
+ * 0 : The first clock transition is the first data capture edge
+ * 1 : The second clock transition is the first data capture edge
+ *
+ * If someone finds this is not the case or sees a logic error with this let me know ;-)
+ */
+ _currentSetting->dataMode = dataMode;
+ uint32_t cr1 = _currentSetting->spi_d->regs->CR1 & ~(SPI_CR1_CPOL|SPI_CR1_CPHA);
+ _currentSetting->spi_d->regs->CR1 = cr1 | (dataMode & (SPI_CR1_CPOL|SPI_CR1_CPHA));
+}
+
+void SPIClass::beginTransaction(uint8_t pin, const SPISettings &settings) {
+ setBitOrder(settings.bitOrder);
+ setDataMode(settings.dataMode);
+ setDataSize(settings.dataSize);
+ setClockDivider(determine_baud_rate(_currentSetting->spi_d, settings.clock));
+ begin();
+}
+
+void SPIClass::beginTransactionSlave(const SPISettings &settings) {
+ setBitOrder(settings.bitOrder);
+ setDataMode(settings.dataMode);
+ setDataSize(settings.dataSize);
+ beginSlave();
+}
+
+void SPIClass::endTransaction() { }
+
+/**
+ * I/O
+ */
+
+uint16_t SPIClass::read() {
+ while (!spi_is_rx_nonempty(_currentSetting->spi_d)) { /* nada */ }
+ return (uint16_t)spi_rx_reg(_currentSetting->spi_d);
+}
+
+void SPIClass::read(uint8_t *buf, uint32_t len) {
+ if (len == 0) return;
+ spi_rx_reg(_currentSetting->spi_d); // clear the RX buffer in case a byte is waiting on it.
+ spi_reg_map * regs = _currentSetting->spi_d->regs;
+ // start sequence: write byte 0
+ regs->DR = 0x00FF; // write the first byte
+ // main loop
+ while (--len) {
+ while (!(regs->SR & SPI_SR_TXE)) { /* nada */ } // wait for TXE flag
+ noInterrupts(); // go atomic level - avoid interrupts to surely get the previously received data
+ regs->DR = 0x00FF; // write the next data item to be transmitted into the SPI_DR register. This clears the TXE flag.
+ while (!(regs->SR & SPI_SR_RXNE)) { /* nada */ } // wait till data is available in the DR register
+ *buf++ = (uint8)(regs->DR); // read and store the received byte. This clears the RXNE flag.
+ interrupts(); // let systick do its job
+ }
+ // read remaining last byte
+ while (!(regs->SR & SPI_SR_RXNE)) { /* nada */ } // wait till data is available in the Rx register
+ *buf++ = (uint8)(regs->DR); // read and store the received byte
+}
+
+void SPIClass::write(uint16_t data) {
+ /* Added for 16bit data Victor Perez. Roger Clark
+ * Improved speed by just directly writing the single byte to the SPI data reg and wait for completion,
+ * by taking the Tx code from transfer(byte)
+ * This almost doubles the speed of this function.
+ */
+ spi_tx_reg(_currentSetting->spi_d, data); // write the data to be transmitted into the SPI_DR register (this clears the TXE flag)
+ waitSpiTxEnd(_currentSetting->spi_d);
+}
+
+void SPIClass::write16(uint16_t data) {
+ // Added by stevestrong: write two consecutive bytes in 8 bit mode (DFF=0)
+ spi_tx_reg(_currentSetting->spi_d, data>>8); // write high byte
+ while (!spi_is_tx_empty(_currentSetting->spi_d)) { /* nada */ } // Wait until TXE=1
+ spi_tx_reg(_currentSetting->spi_d, data); // write low byte
+ waitSpiTxEnd(_currentSetting->spi_d);
+}
+
+void SPIClass::write(uint16_t data, uint32_t n) {
+ // Added by stevstrong: Repeatedly send same data by the specified number of times
+ spi_reg_map * regs = _currentSetting->spi_d->regs;
+ while (n--) {
+ regs->DR = data; // write the data to be transmitted into the SPI_DR register (this clears the TXE flag)
+ while (!(regs->SR & SPI_SR_TXE)) { /* nada */ } // wait till Tx empty
+ }
+ while (regs->SR & SPI_SR_BSY) { /* nada */ } // wait until BSY=0 before returning
+}
+
+void SPIClass::write(const void *data, uint32_t length) {
+ spi_dev * spi_d = _currentSetting->spi_d;
+ spi_tx(spi_d, data, length); // data can be array of bytes or words
+ waitSpiTxEnd(spi_d);
+}
+
+uint8_t SPIClass::transfer(uint8_t byte) const {
+ spi_dev * spi_d = _currentSetting->spi_d;
+ spi_rx_reg(spi_d); // read any previous data
+ spi_tx_reg(spi_d, byte); // Write the data item to be transmitted into the SPI_DR register
+ waitSpiTxEnd(spi_d);
+ return (uint8)spi_rx_reg(spi_d); // "... and read the last received data."
+}
+
+uint16_t SPIClass::transfer16(uint16_t data) const {
+ // Modified by stevestrong: write & read two consecutive bytes in 8 bit mode (DFF=0)
+ // This is more effective than two distinct byte transfers
+ spi_dev * spi_d = _currentSetting->spi_d;
+ spi_rx_reg(spi_d); // read any previous data
+ spi_tx_reg(spi_d, data>>8); // write high byte
+ waitSpiTxEnd(spi_d); // wait until TXE=1 and then wait until BSY=0
+ uint16_t ret = spi_rx_reg(spi_d)<<8; // read and shift high byte
+ spi_tx_reg(spi_d, data); // write low byte
+ waitSpiTxEnd(spi_d); // wait until TXE=1 and then wait until BSY=0
+ ret += spi_rx_reg(spi_d); // read low byte
+ return ret;
+}
+
+/**
+ * Roger Clark and Victor Perez, 2015
+ * Performs a DMA SPI transfer with at least a receive buffer.
+ * If a TX buffer is not provided, FF is sent over and over for the length of the transfer.
+ * On exit TX buffer is not modified, and RX buffer contains the received data.
+ * Still in progress.
+ */
+void SPIClass::dmaTransferSet(const void *transmitBuf, void *receiveBuf) {
+ dma_init(_currentSetting->spiDmaDev);
+ //spi_rx_dma_enable(_currentSetting->spi_d);
+ //spi_tx_dma_enable(_currentSetting->spi_d);
+ dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_16BIT) ? DMA_SIZE_16BITS : DMA_SIZE_8BITS;
+ dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, &_currentSetting->spi_d->regs->DR,
+ dma_bit_size, receiveBuf, dma_bit_size, (DMA_MINC_MODE | DMA_TRNS_CMPLT ));// receive buffer DMA
+ if (!transmitBuf) {
+ transmitBuf = &ff;
+ dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR,
+ dma_bit_size, (volatile void*)transmitBuf, dma_bit_size, (DMA_FROM_MEM));// Transmit FF repeatedly
+ }
+ else {
+ dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR,
+ dma_bit_size, (volatile void*)transmitBuf, dma_bit_size, (DMA_MINC_MODE | DMA_FROM_MEM ));// Transmit buffer DMA
+ }
+ dma_set_priority(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, DMA_PRIORITY_LOW);
+ dma_set_priority(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, DMA_PRIORITY_VERY_HIGH);
+}
+
+uint8_t SPIClass::dmaTransferRepeat(uint16_t length) {
+ if (length == 0) return 0;
+ if (spi_is_rx_nonempty(_currentSetting->spi_d) == 1) spi_rx_reg(_currentSetting->spi_d);
+ _currentSetting->state = SPI_STATE_TRANSFER;
+ dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, length);
+ dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length);
+ dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel);// enable receive
+ dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit
+ spi_rx_dma_enable(_currentSetting->spi_d);
+ spi_tx_dma_enable(_currentSetting->spi_d);
+ if (_currentSetting->receiveCallback)
+ return 0;
+
+ //uint32_t m = millis();
+ uint8_t b = 0;
+ uint32_t m = millis();
+ while (!(dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & DMA_ISR_TCIF1)) {
+ // Avoid interrupts and just loop waiting for the flag to be set.
+ if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; }
+ }
+
+ waitSpiTxEnd(_currentSetting->spi_d); // until TXE=1 and BSY=0
+ spi_tx_dma_disable(_currentSetting->spi_d);
+ spi_rx_dma_disable(_currentSetting->spi_d);
+ dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);
+ dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel);
+ dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel);
+ dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);
+ _currentSetting->state = SPI_STATE_READY;
+ return b;
+}
+
+/**
+ * Roger Clark and Victor Perez, 2015
+ * Performs a DMA SPI transfer with at least a receive buffer.
+ * If a TX buffer is not provided, FF is sent over and over for the length of the transfer.
+ * On exit TX buffer is not modified, and RX buffer contains the received data.
+ * Still in progress.
+ */
+uint8_t SPIClass::dmaTransfer(const void *transmitBuf, void *receiveBuf, uint16_t length) {
+ dmaTransferSet(transmitBuf, receiveBuf);
+ return dmaTransferRepeat(length);
+}
+
+/**
+ * Roger Clark and Victor Perez, 2015
+ * Performs a DMA SPI send using a TX buffer.
+ * On exit TX buffer is not modified.
+ * Still in progress.
+ * 2016 - stevstrong - reworked to automatically detect bit size from SPI setting
+ */
+void SPIClass::dmaSendSet(const void * transmitBuf, bool minc) {
+ uint32_t flags = ( (DMA_MINC_MODE*minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT);
+ dma_init(_currentSetting->spiDmaDev);
+ dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_16BIT) ? DMA_SIZE_16BITS : DMA_SIZE_8BITS;
+ dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR, dma_bit_size,
+ (volatile void*)transmitBuf, dma_bit_size, flags);// Transmit buffer DMA
+ dma_set_priority(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, DMA_PRIORITY_LOW);
+}
+
+uint8_t SPIClass::dmaSendRepeat(uint16_t length) {
+ if (length == 0) return 0;
+
+ dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);
+ dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length);
+ _currentSetting->state = SPI_STATE_TRANSMIT;
+ dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel); // enable transmit
+ spi_tx_dma_enable(_currentSetting->spi_d);
+ if (_currentSetting->transmitCallback) return 0;
+
+ uint32_t m = millis();
+ uint8_t b = 0;
+ while (!(dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & DMA_ISR_TCIF1)) {
+ // Avoid interrupts and just loop waiting for the flag to be set.
+ if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; }
+ }
+ waitSpiTxEnd(_currentSetting->spi_d); // until TXE=1 and BSY=0
+ spi_tx_dma_disable(_currentSetting->spi_d);
+ dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);
+ dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);
+ _currentSetting->state = SPI_STATE_READY;
+ return b;
+}
+
+uint8_t SPIClass::dmaSend(const void * transmitBuf, uint16_t length, bool minc) {
+ dmaSendSet(transmitBuf, minc);
+ return dmaSendRepeat(length);
+}
+
+uint8_t SPIClass::dmaSendAsync(const void * transmitBuf, uint16_t length, bool minc) {
+ uint8_t b = 0;
+
+ if (_currentSetting->state != SPI_STATE_READY) {
+ uint32_t m = millis();
+ while (!(dma_get_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel) & DMA_ISR_TCIF1)) {
+ //Avoid interrupts and just loop waiting for the flag to be set.
+ //delayMicroseconds(10);
+ if ((millis() - m) > DMA_TIMEOUT) { b = 2; break; }
+ }
+ waitSpiTxEnd(_currentSetting->spi_d); // until TXE=1 and BSY=0
+ spi_tx_dma_disable(_currentSetting->spi_d);
+ dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);
+ _currentSetting->state = SPI_STATE_READY;
+ }
+
+ if (length == 0) return 0;
+ uint32_t flags = ( (DMA_MINC_MODE*minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT);
+
+ dma_init(_currentSetting->spiDmaDev);
+ // TX
+ dma_xfer_size dma_bit_size = (_currentSetting->dataSize==DATA_SIZE_16BIT) ? DMA_SIZE_16BITS : DMA_SIZE_8BITS;
+ dma_setup_transfer(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &_currentSetting->spi_d->regs->DR,
+ dma_bit_size, (volatile void*)transmitBuf, dma_bit_size, flags);// Transmit buffer DMA
+ dma_set_num_transfers(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, length);
+ dma_clear_isr_bits(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);
+ dma_enable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);// enable transmit
+ spi_tx_dma_enable(_currentSetting->spi_d);
+
+ _currentSetting->state = SPI_STATE_TRANSMIT;
+ return b;
+}
+
+
+/**
+ * New functions added to manage callbacks.
+ * Victor Perez 2017
+ */
+void SPIClass::onReceive(void(*callback)()) {
+ _currentSetting->receiveCallback = callback;
+ if (callback) {
+ switch (_currentSetting->spi_d->clk_id) {
+ #if BOARD_NR_SPI >= 1
+ case RCC_SPI1:
+ dma_attach_interrupt(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, &SPIClass::_spi1EventCallback);
+ break;
+ #endif
+ #if BOARD_NR_SPI >= 2
+ case RCC_SPI2:
+ dma_attach_interrupt(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, &SPIClass::_spi2EventCallback);
+ break;
+ #endif
+ #if BOARD_NR_SPI >= 3
+ case RCC_SPI3:
+ dma_attach_interrupt(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel, &SPIClass::_spi3EventCallback);
+ break;
+ #endif
+ default: ASSERT(0);
+ }
+ }
+ else {
+ dma_detach_interrupt(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel);
+ }
+}
+
+void SPIClass::onTransmit(void(*callback)()) {
+ _currentSetting->transmitCallback = callback;
+ if (callback) {
+ switch (_currentSetting->spi_d->clk_id) {
+ #if BOARD_NR_SPI >= 1
+ case RCC_SPI1:
+ dma_attach_interrupt(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &SPIClass::_spi1EventCallback);
+ break;
+ #endif
+ #if BOARD_NR_SPI >= 2
+ case RCC_SPI2:
+ dma_attach_interrupt(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &SPIClass::_spi2EventCallback);
+ break;
+ #endif
+ #if BOARD_NR_SPI >= 3
+ case RCC_SPI3:
+ dma_attach_interrupt(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel, &SPIClass::_spi3EventCallback);
+ break;
+ #endif
+ default: ASSERT(0);
+ }
+ }
+ else {
+ dma_detach_interrupt(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);
+ }
+}
+
+/**
+ * TODO: check if better to first call the customer code, next disable the DMA requests.
+ * Also see if we need to check whether callbacks are set or not, may be better to be checked
+ * during the initial setup and only set the callback to EventCallback if they are set.
+ */
+void SPIClass::EventCallback() {
+ waitSpiTxEnd(_currentSetting->spi_d);
+ switch (_currentSetting->state) {
+ case SPI_STATE_TRANSFER:
+ while (spi_is_rx_nonempty(_currentSetting->spi_d)) { /* nada */ }
+ _currentSetting->state = SPI_STATE_READY;
+ spi_tx_dma_disable(_currentSetting->spi_d);
+ spi_rx_dma_disable(_currentSetting->spi_d);
+ //dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);
+ //dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiRxDmaChannel);
+ if (_currentSetting->receiveCallback)
+ _currentSetting->receiveCallback();
+ break;
+ case SPI_STATE_TRANSMIT:
+ _currentSetting->state = SPI_STATE_READY;
+ spi_tx_dma_disable(_currentSetting->spi_d);
+ //dma_disable(_currentSetting->spiDmaDev, _currentSetting->spiTxDmaChannel);
+ if (_currentSetting->transmitCallback)
+ _currentSetting->transmitCallback();
+ break;
+ default:
+ break;
+ }
+}
+
+void SPIClass::attachInterrupt() {
+ // Should be enableInterrupt()
+}
+
+void SPIClass::detachInterrupt() {
+ // Should be disableInterrupt()
+}
+
+/**
+ * Pin accessors
+ */
+
+uint8_t SPIClass::misoPin() {
+ return dev_to_spi_pins(_currentSetting->spi_d)->miso;
+}
+
+uint8_t SPIClass::mosiPin() {
+ return dev_to_spi_pins(_currentSetting->spi_d)->mosi;
+}
+
+uint8_t SPIClass::sckPin() {
+ return dev_to_spi_pins(_currentSetting->spi_d)->sck;
+}
+
+uint8_t SPIClass::nssPin() {
+ return dev_to_spi_pins(_currentSetting->spi_d)->nss;
+}
+
+/**
+ * Deprecated functions
+ */
+uint8_t SPIClass::send(uint8_t data) { write(data); return 1; }
+uint8_t SPIClass::send(uint8_t *buf, uint32_t len) { write(buf, len); return len; }
+uint8_t SPIClass::recv() { return read(); }
+
+/**
+ * DMA call back functions, one per port.
+ */
+#if BOARD_NR_SPI >= 1
+ void SPIClass::_spi1EventCallback() {
+ reinterpret_cast(_spi1_this)->EventCallback();
+ }
+#endif
+#if BOARD_NR_SPI >= 2
+ void SPIClass::_spi2EventCallback() {
+ reinterpret_cast(_spi2_this)->EventCallback();
+ }
+#endif
+#if BOARD_NR_SPI >= 3
+ void SPIClass::_spi3EventCallback() {
+ reinterpret_cast(_spi3_this)->EventCallback();
+ }
+#endif
+
+/**
+ * Auxiliary functions
+ */
+static const spi_pins* dev_to_spi_pins(spi_dev *dev) {
+ switch (dev->clk_id) {
+ #if BOARD_NR_SPI >= 1
+ case RCC_SPI1: return board_spi_pins;
+ #endif
+ #if BOARD_NR_SPI >= 2
+ case RCC_SPI2: return board_spi_pins + 1;
+ #endif
+ #if BOARD_NR_SPI >= 3
+ case RCC_SPI3: return board_spi_pins + 2;
+ #endif
+ default: return nullptr;
+ }
+}
+
+static void disable_pwm(const stm32_pin_info *i) {
+ if (i->timer_device)
+ timer_set_mode(i->timer_device, i->timer_channel, TIMER_DISABLED);
+}
+
+static void configure_gpios(spi_dev *dev, bool as_master) {
+ const spi_pins *pins = dev_to_spi_pins(dev);
+ if (!pins) return;
+
+ const stm32_pin_info *nssi = &PIN_MAP[pins->nss],
+ *scki = &PIN_MAP[pins->sck],
+ *misoi = &PIN_MAP[pins->miso],
+ *mosii = &PIN_MAP[pins->mosi];
+
+ disable_pwm(nssi);
+ disable_pwm(scki);
+ disable_pwm(misoi);
+ disable_pwm(mosii);
+
+ spi_config_gpios(dev, as_master, nssi->gpio_device, nssi->gpio_bit,
+ scki->gpio_device, scki->gpio_bit, misoi->gpio_bit,
+ mosii->gpio_bit);
+}
+
+static const spi_baud_rate baud_rates[8] __FLASH__ = {
+ SPI_BAUD_PCLK_DIV_2,
+ SPI_BAUD_PCLK_DIV_4,
+ SPI_BAUD_PCLK_DIV_8,
+ SPI_BAUD_PCLK_DIV_16,
+ SPI_BAUD_PCLK_DIV_32,
+ SPI_BAUD_PCLK_DIV_64,
+ SPI_BAUD_PCLK_DIV_128,
+ SPI_BAUD_PCLK_DIV_256,
+};
+
+/**
+ * Note: This assumes you're on a LeafLabs-style board
+ * (CYCLES_PER_MICROSECOND == 72, APB2 at 72MHz, APB1 at 36MHz).
+ */
+static spi_baud_rate determine_baud_rate(spi_dev *dev, uint32_t freq) {
+ uint32_t clock = 0;
+ switch (rcc_dev_clk(dev->clk_id)) {
+ case RCC_AHB:
+ case RCC_APB2: clock = STM32_PCLK2; break; // 72 Mhz
+ case RCC_APB1: clock = STM32_PCLK1; break; // 36 Mhz
+ }
+ clock >>= 1;
+
+ uint8_t i = 0;
+ while (i < 7 && freq < clock) { clock >>= 1; i++; }
+ return baud_rates[i];
+}
+
+SPIClass SPI(SPI_DEVICE);
+
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/SPI.h b/Marlin/src/HAL/STM32F1/SPI.h
new file mode 100644
index 0000000000..0941fa55b7
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/SPI.h
@@ -0,0 +1,426 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2010 Perry Hung.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
+#pragma once
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+// Number of SPI ports
+#ifdef BOARD_SPI3_SCK_PIN
+ #define BOARD_NR_SPI 3
+#elif defined(BOARD_SPI2_SCK_PIN)
+ #define BOARD_NR_SPI 2
+#elif defined(BOARD_SPI1_SCK_PIN)
+ #define BOARD_NR_SPI 1
+#endif
+
+// SPI_HAS_TRANSACTION means SPI has
+// - beginTransaction()
+// - endTransaction()
+// - usingInterrupt()
+// - SPISetting(clock, bitOrder, dataMode)
+//#define SPI_HAS_TRANSACTION
+
+#define SPI_CLOCK_DIV2 SPI_BAUD_PCLK_DIV_2
+#define SPI_CLOCK_DIV4 SPI_BAUD_PCLK_DIV_4
+#define SPI_CLOCK_DIV8 SPI_BAUD_PCLK_DIV_8
+#define SPI_CLOCK_DIV16 SPI_BAUD_PCLK_DIV_16
+#define SPI_CLOCK_DIV32 SPI_BAUD_PCLK_DIV_32
+#define SPI_CLOCK_DIV64 SPI_BAUD_PCLK_DIV_64
+#define SPI_CLOCK_DIV128 SPI_BAUD_PCLK_DIV_128
+#define SPI_CLOCK_DIV256 SPI_BAUD_PCLK_DIV_256
+
+/*
+ * Roger Clark. 20150106
+ * Commented out redundant AVR defined
+ *
+#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR
+#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR
+#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR
+
+// define SPI_AVR_EIMSK for AVR boards with external interrupt pins
+#ifdef EIMSK
+ #define SPI_AVR_EIMSK EIMSK
+#elif defined(GICR)
+ #define SPI_AVR_EIMSK GICR
+#elif defined(GIMSK)
+ #define SPI_AVR_EIMSK GIMSK
+#endif
+*/
+
+#ifndef STM32_LSBFIRST
+ #define STM32_LSBFIRST 0
+#endif
+#ifndef STM32_MSBFIRST
+ #define STM32_MSBFIRST 1
+#endif
+
+// PC13 or PA4
+#define BOARD_SPI_DEFAULT_SS PA4
+//#define BOARD_SPI_DEFAULT_SS PC13
+
+#define SPI_MODE0 SPI_MODE_0
+#define SPI_MODE1 SPI_MODE_1
+#define SPI_MODE2 SPI_MODE_2
+#define SPI_MODE3 SPI_MODE_3
+
+#define DATA_SIZE_8BIT SPI_CR1_DFF_8_BIT
+#define DATA_SIZE_16BIT SPI_CR1_DFF_16_BIT
+
+typedef enum {
+ SPI_STATE_IDLE,
+ SPI_STATE_READY,
+ SPI_STATE_RECEIVE,
+ SPI_STATE_TRANSMIT,
+ SPI_STATE_TRANSFER
+} spi_mode_t;
+
+class SPISettings {
+public:
+ SPISettings(uint32_t inClock, BitOrder inBitOrder, uint8_t inDataMode) {
+ if (__builtin_constant_p(inClock))
+ init_AlwaysInline(inClock, inBitOrder, inDataMode, DATA_SIZE_8BIT);
+ else
+ init_MightInline(inClock, inBitOrder, inDataMode, DATA_SIZE_8BIT);
+ }
+ SPISettings(uint32_t inClock, BitOrder inBitOrder, uint8_t inDataMode, uint32_t inDataSize) {
+ if (__builtin_constant_p(inClock))
+ init_AlwaysInline(inClock, inBitOrder, inDataMode, inDataSize);
+ else
+ init_MightInline(inClock, inBitOrder, inDataMode, inDataSize);
+ }
+ SPISettings(uint32_t inClock) {
+ if (__builtin_constant_p(inClock))
+ init_AlwaysInline(inClock, MSBFIRST, SPI_MODE0, DATA_SIZE_8BIT);
+ else
+ init_MightInline(inClock, MSBFIRST, SPI_MODE0, DATA_SIZE_8BIT);
+ }
+ SPISettings() {
+ init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0, DATA_SIZE_8BIT);
+ }
+private:
+ void init_MightInline(uint32_t inClock, BitOrder inBitOrder, uint8_t inDataMode, uint32_t inDataSize) {
+ init_AlwaysInline(inClock, inBitOrder, inDataMode, inDataSize);
+ }
+ void init_AlwaysInline(uint32_t inClock, BitOrder inBitOrder, uint8_t inDataMode, uint32_t inDataSize) __attribute__((__always_inline__)) {
+ clock = inClock;
+ bitOrder = inBitOrder;
+ dataMode = inDataMode;
+ dataSize = inDataSize;
+ //state = SPI_STATE_IDLE;
+ }
+ uint32_t clock;
+ uint32_t dataSize;
+ uint32_t clockDivider;
+ BitOrder bitOrder;
+ uint8_t dataMode;
+ uint8_t _SSPin;
+ volatile spi_mode_t state;
+ spi_dev *spi_d;
+ dma_channel spiRxDmaChannel, spiTxDmaChannel;
+ dma_dev* spiDmaDev;
+ void (*receiveCallback)() = nullptr;
+ void (*transmitCallback)() = nullptr;
+
+ friend class SPIClass;
+};
+
+/*
+ * Kept for compat.
+ */
+static const uint8_t ff = 0xFF;
+
+/**
+ * @brief Wirish SPI interface.
+ *
+ * This implementation uses software slave management, so the caller
+ * is responsible for controlling the slave select line.
+ */
+class SPIClass {
+
+public:
+ /**
+ * @param spiPortNumber Number of the SPI port to manage.
+ */
+ SPIClass(uint32_t spiPortNumber);
+
+ /**
+ * Init using pins
+ */
+ SPIClass(int8_t mosi, int8_t miso, int8_t sclk, int8_t ssel=-1);
+
+ /**
+ * @brief Equivalent to begin(SPI_1_125MHZ, MSBFIRST, 0).
+ */
+ void begin();
+
+ /**
+ * @brief Turn on a SPI port and set its GPIO pin modes for use as a slave.
+ *
+ * SPI port is enabled in full duplex mode, with software slave management.
+ *
+ * @param bitOrder Either LSBFIRST (little-endian) or MSBFIRST(big-endian)
+ * @param mode SPI mode to use
+ */
+ void beginSlave(uint32_t bitOrder, uint32_t mode);
+
+ /**
+ * @brief Equivalent to beginSlave(MSBFIRST, 0).
+ */
+ void beginSlave();
+
+ /**
+ * @brief Disables the SPI port, but leaves its GPIO pin modes unchanged.
+ */
+ void end();
+
+ void beginTransaction(const SPISettings &settings) { beginTransaction(BOARD_SPI_DEFAULT_SS, settings); }
+ void beginTransaction(uint8_t pin, const SPISettings &settings);
+ void endTransaction();
+
+ void beginTransactionSlave(const SPISettings &settings);
+
+ void setClockDivider(uint32_t clockDivider);
+ void setBitOrder(BitOrder bitOrder);
+ void setDataMode(uint8_t dataMode);
+
+ // SPI Configuration methods
+ void attachInterrupt();
+ void detachInterrupt();
+
+ /* Victor Perez. Added to change datasize from 8 to 16 bit modes on the fly.
+ * Input parameter should be SPI_CR1_DFF set to 0 or 1 on a 32bit word.
+ * Requires an added function spi_data_size on STM32F1 / cores / maple / libmaple / spi.c
+ */
+ void setDataSize(uint32_t ds);
+
+ uint32_t getDataSize() { return _currentSetting->dataSize; }
+
+ /* Victor Perez 2017. Added to set and clear callback functions for callback
+ * on DMA transfer completion.
+ * onReceive used to set the callback in case of dmaTransfer (tx/rx), once rx is completed
+ * onTransmit used to set the callback in case of dmaSend (tx only). That function
+ * will NOT be called in case of TX/RX
+ */
+ void onReceive(void(*)());
+ void onTransmit(void(*)());
+
+ /*
+ * I/O
+ */
+
+ /**
+ * @brief Return the next unread byte/word.
+ *
+ * If there is no unread byte/word waiting, this function will block
+ * until one is received.
+ */
+ uint16_t read();
+
+ /**
+ * @brief Read length bytes, storing them into buffer.
+ * @param buffer Buffer to store received bytes into.
+ * @param length Number of bytes to store in buffer. This
+ * function will block until the desired number of
+ * bytes have been read.
+ */
+ void read(uint8_t *buffer, uint32_t length);
+
+ /**
+ * @brief Transmit one byte/word.
+ * @param data to transmit.
+ */
+ void write(uint16_t data);
+ void write16(uint16_t data); // write 2 bytes in 8 bit mode (DFF=0)
+
+ /**
+ * @brief Transmit one byte/word a specified number of times.
+ * @param data to transmit.
+ */
+ void write(uint16_t data, uint32_t n);
+
+ /**
+ * @brief Transmit multiple bytes/words.
+ * @param buffer Bytes/words to transmit.
+ * @param length Number of bytes/words in buffer to transmit.
+ */
+ void write(const void * buffer, uint32_t length);
+
+ /**
+ * @brief Transmit a byte, then return the next unread byte.
+ *
+ * This function transmits before receiving.
+ *
+ * @param data Byte to transmit.
+ * @return Next unread byte.
+ */
+ uint8_t transfer(uint8_t data) const;
+ uint16_t transfer16(uint16_t data) const;
+
+ /**
+ * @brief Sets up a DMA Transfer for "length" bytes.
+ * The transfer mode (8 or 16 bit mode) is evaluated from the SPI peripheral setting.
+ *
+ * This function transmits and receives to buffers.
+ *
+ * @param transmitBuf buffer Bytes to transmit. If passed as 0, it sends FF repeatedly for "length" bytes
+ * @param receiveBuf buffer Bytes to save received data.
+ * @param length Number of bytes in buffer to transmit.
+ */
+ uint8_t dmaTransfer(const void * transmitBuf, void * receiveBuf, uint16_t length);
+ void dmaTransferSet(const void *transmitBuf, void *receiveBuf);
+ uint8_t dmaTransferRepeat(uint16_t length);
+
+ /**
+ * @brief Sets up a DMA Transmit for SPI 8 or 16 bit transfer mode.
+ * The transfer mode (8 or 16 bit mode) is evaluated from the SPI peripheral setting.
+ *
+ * This function only transmits and does not care about the RX fifo.
+ *
+ * @param data buffer half words to transmit,
+ * @param length Number of bytes in buffer to transmit.
+ * @param minc Set to use Memory Increment mode, clear to use Circular mode.
+ */
+ uint8_t dmaSend(const void * transmitBuf, uint16_t length, bool minc = 1);
+ void dmaSendSet(const void * transmitBuf, bool minc);
+ uint8_t dmaSendRepeat(uint16_t length);
+
+ uint8_t dmaSendAsync(const void * transmitBuf, uint16_t length, bool minc = 1);
+ /*
+ * Pin accessors
+ */
+
+ /**
+ * @brief Return the number of the MISO (master in, slave out) pin
+ */
+ uint8_t misoPin();
+
+ /**
+ * @brief Return the number of the MOSI (master out, slave in) pin
+ */
+ uint8_t mosiPin();
+
+ /**
+ * @brief Return the number of the SCK (serial clock) pin
+ */
+ uint8_t sckPin();
+
+ /**
+ * @brief Return the number of the NSS (slave select) pin
+ */
+ uint8_t nssPin();
+
+ /* Escape hatch */
+
+ /**
+ * @brief Get a pointer to the underlying libmaple spi_dev for
+ * this HardwareSPI instance.
+ */
+ spi_dev* c_dev() { return _currentSetting->spi_d; }
+
+ spi_dev* dev() { return _currentSetting->spi_d; }
+
+ /**
+ * @brief Sets the number of the SPI peripheral to be used by
+ * this HardwareSPI instance.
+ *
+ * @param spi_num Number of the SPI port. 1-2 in low density devices
+ * or 1-3 in high density devices.
+ */
+ void setModule(int spi_num) {
+ _currentSetting = &_settings[spi_num - 1];// SPI channels are called 1 2 and 3 but the array is zero indexed
+ }
+
+ /* -- The following methods are deprecated --------------------------- */
+
+ /**
+ * @brief Deprecated.
+ *
+ * Use HardwareSPI::transfer() instead.
+ *
+ * @see HardwareSPI::transfer()
+ */
+ uint8_t send(uint8_t data);
+
+ /**
+ * @brief Deprecated.
+ *
+ * Use HardwareSPI::write() in combination with
+ * HardwareSPI::read() (or HardwareSPI::transfer()) instead.
+ *
+ * @see HardwareSPI::write()
+ * @see HardwareSPI::read()
+ * @see HardwareSPI::transfer()
+ */
+ uint8_t send(uint8_t *data, uint32_t length);
+
+ /**
+ * @brief Deprecated.
+ *
+ * Use HardwareSPI::read() instead.
+ *
+ * @see HardwareSPI::read()
+ */
+ uint8_t recv();
+
+private:
+
+ SPISettings _settings[BOARD_NR_SPI];
+ SPISettings *_currentSetting;
+
+ void updateSettings();
+
+ /*
+ * Functions added for DMA transfers with Callback.
+ * Experimental.
+ */
+
+ void EventCallback();
+
+ #if BOARD_NR_SPI >= 1
+ static void _spi1EventCallback();
+ #endif
+ #if BOARD_NR_SPI >= 2
+ static void _spi2EventCallback();
+ #endif
+ #if BOARD_NR_SPI >= 3
+ static void _spi3EventCallback();
+ #endif
+ /*
+ spi_dev *spi_d;
+ uint8_t _SSPin;
+ uint32_t clockDivider;
+ uint8_t dataMode;
+ BitOrder bitOrder;
+ */
+};
+
+extern SPIClass SPI;
diff --git a/Marlin/src/HAL/STM32F1/Servo.cpp b/Marlin/src/HAL/STM32F1/Servo.cpp
new file mode 100644
index 0000000000..47ffb631cf
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/Servo.cpp
@@ -0,0 +1,226 @@
+/**
+ * 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
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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 .
+ *
+ */
+#ifdef __STM32F1__
+
+#include "../../inc/MarlinConfig.h"
+
+#if HAS_SERVOS
+
+uint8_t ServoCount = 0;
+
+#include "Servo.h"
+
+//#include "Servo.h"
+
+#include
+#include
+#include
+#include
+
+/**
+ * 20 millisecond period config. For a 1-based prescaler,
+ *
+ * (prescaler * overflow / CYC_MSEC) msec = 1 timer cycle = 20 msec
+ * => prescaler * overflow = 20 * CYC_MSEC
+ *
+ * This uses the smallest prescaler that allows an overflow < 2^16.
+ */
+#define MAX_OVERFLOW UINT16_MAX // _BV(16) - 1
+#define CYC_MSEC (1000 * CYCLES_PER_MICROSECOND)
+#define TAU_MSEC 20
+#define TAU_USEC (TAU_MSEC * 1000)
+#define TAU_CYC (TAU_MSEC * CYC_MSEC)
+#define SERVO_PRESCALER (TAU_CYC / MAX_OVERFLOW + 1)
+#define SERVO_OVERFLOW ((uint16_t)round((double)TAU_CYC / SERVO_PRESCALER))
+
+// Unit conversions
+#define US_TO_COMPARE(us) uint16_t(map((us), 0, TAU_USEC, 0, SERVO_OVERFLOW))
+#define COMPARE_TO_US(c) uint32_t(map((c), 0, SERVO_OVERFLOW, 0, TAU_USEC))
+#define ANGLE_TO_US(a) uint16_t(map((a), minAngle, maxAngle, SERVO_DEFAULT_MIN_PW, SERVO_DEFAULT_MAX_PW))
+#define US_TO_ANGLE(us) int16_t(map((us), SERVO_DEFAULT_MIN_PW, SERVO_DEFAULT_MAX_PW, minAngle, maxAngle))
+
+void libServo::servoWrite(uint8_t inPin, uint16_t duty_cycle) {
+ #ifdef MF_TIMER_SERVO0
+ if (servoIndex == 0) {
+ pwmSetDuty(duty_cycle);
+ return;
+ }
+ #endif
+
+ timer_dev *tdev = PIN_MAP[inPin].timer_device;
+ uint8_t tchan = PIN_MAP[inPin].timer_channel;
+ if (tdev) timer_set_compare(tdev, tchan, duty_cycle);
+}
+
+libServo::libServo() {
+ servoIndex = ServoCount < MAX_SERVOS ? ServoCount++ : INVALID_SERVO;
+ HAL_timer_set_interrupt_priority(MF_TIMER_SERVO0, SERVO0_TIMER_IRQ_PRIO);
+}
+
+bool libServo::attach(const int32_t inPin, const int32_t inMinAngle, const int32_t inMaxAngle) {
+ if (servoIndex >= MAX_SERVOS) return false;
+ if (inPin >= BOARD_NR_GPIO_PINS) return false;
+
+ minAngle = inMinAngle;
+ maxAngle = inMaxAngle;
+ angle = -1;
+
+ #ifdef MF_TIMER_SERVO0
+ if (servoIndex == 0 && setupSoftPWM(inPin)) {
+ pin = inPin; // set attached()
+ return true;
+ }
+ #endif
+
+ if (!PWM_PIN(inPin)) return false;
+
+ timer_dev *tdev = PIN_MAP[inPin].timer_device;
+ //uint8_t tchan = PIN_MAP[inPin].timer_channel;
+
+ SET_PWM(inPin);
+ servoWrite(inPin, 0);
+
+ timer_pause(tdev);
+ timer_set_prescaler(tdev, SERVO_PRESCALER - 1); // prescaler is 1-based
+ timer_set_reload(tdev, SERVO_OVERFLOW);
+ timer_generate_update(tdev);
+ timer_resume(tdev);
+
+ pin = inPin; // set attached()
+ return true;
+}
+
+bool libServo::detach() {
+ if (!attached()) return false;
+ angle = -1;
+ servoWrite(pin, 0);
+ return true;
+}
+
+int32_t libServo::read() const {
+ if (attached()) {
+ #ifdef MF_TIMER_SERVO0
+ if (servoIndex == 0) return angle;
+ #endif
+ timer_dev *tdev = PIN_MAP[pin].timer_device;
+ uint8_t tchan = PIN_MAP[pin].timer_channel;
+ return US_TO_ANGLE(COMPARE_TO_US(timer_get_compare(tdev, tchan)));
+ }
+ return 0;
+}
+
+void libServo::move(const int32_t value) {
+ constexpr uint16_t servo_delay[] = SERVO_DELAY;
+ static_assert(COUNT(servo_delay) == NUM_SERVOS, "SERVO_DELAY must be an array NUM_SERVOS long.");
+
+ if (attached()) {
+ angle = constrain(value, minAngle, maxAngle);
+ servoWrite(pin, US_TO_COMPARE(ANGLE_TO_US(angle)));
+ safe_delay(servo_delay[servoIndex]);
+ TERN_(DEACTIVATE_SERVOS_AFTER_MOVE, detach());
+ }
+}
+
+#ifdef MF_TIMER_SERVO0
+ extern "C" void Servo_IRQHandler() {
+ static timer_dev *tdev = HAL_get_timer_dev(MF_TIMER_SERVO0);
+ uint16_t SR = timer_get_status(tdev);
+ if (SR & TIMER_SR_CC1IF) { // channel 1 off
+ #ifdef SERVO0_PWM_OD
+ OUT_WRITE_OD(SERVO0_PIN, HIGH); // off
+ #else
+ OUT_WRITE(SERVO0_PIN, LOW);
+ #endif
+ timer_reset_status_bit(tdev, TIMER_SR_CC1IF_BIT);
+ }
+ if (SR & TIMER_SR_CC2IF) { // channel 2 resume
+ #ifdef SERVO0_PWM_OD
+ OUT_WRITE_OD(SERVO0_PIN, LOW); // on
+ #else
+ OUT_WRITE(SERVO0_PIN, HIGH);
+ #endif
+ timer_reset_status_bit(tdev, TIMER_SR_CC2IF_BIT);
+ }
+ }
+
+ bool libServo::setupSoftPWM(const int32_t inPin) {
+ timer_dev *tdev = HAL_get_timer_dev(MF_TIMER_SERVO0);
+ if (!tdev) return false;
+ #ifdef SERVO0_PWM_OD
+ OUT_WRITE_OD(inPin, HIGH);
+ #else
+ OUT_WRITE(inPin, LOW);
+ #endif
+
+ timer_pause(tdev);
+ timer_set_mode(tdev, 1, TIMER_OUTPUT_COMPARE); // counter with isr
+ timer_oc_set_mode(tdev, 1, TIMER_OC_MODE_FROZEN, 0); // no pin output change
+ timer_oc_set_mode(tdev, 2, TIMER_OC_MODE_FROZEN, 0); // no pin output change
+ timer_set_prescaler(tdev, SERVO_PRESCALER - 1); // prescaler is 1-based
+ timer_set_reload(tdev, SERVO_OVERFLOW);
+ timer_set_compare(tdev, 1, SERVO_OVERFLOW);
+ timer_set_compare(tdev, 2, SERVO_OVERFLOW);
+ timer_attach_interrupt(tdev, 1, Servo_IRQHandler);
+ timer_attach_interrupt(tdev, 2, Servo_IRQHandler);
+ timer_generate_update(tdev);
+ timer_resume(tdev);
+
+ return true;
+ }
+
+ void libServo::pwmSetDuty(const uint16_t duty_cycle) {
+ timer_dev *tdev = HAL_get_timer_dev(MF_TIMER_SERVO0);
+ timer_set_compare(tdev, 1, duty_cycle);
+ timer_generate_update(tdev);
+ if (duty_cycle) {
+ timer_enable_irq(tdev, 1);
+ timer_enable_irq(tdev, 2);
+ }
+ else {
+ timer_disable_irq(tdev, 1);
+ timer_disable_irq(tdev, 2);
+ #ifdef SERVO0_PWM_OD
+ OUT_WRITE_OD(pin, HIGH); // off
+ #else
+ OUT_WRITE(pin, LOW);
+ #endif
+ }
+ }
+
+ void libServo::pauseSoftPWM() { // detach
+ timer_dev *tdev = HAL_get_timer_dev(MF_TIMER_SERVO0);
+ timer_pause(tdev);
+ pwmSetDuty(0);
+ }
+
+#else
+
+ bool libServo::setupSoftPWM(const int32_t inPin) { return false; }
+ void libServo::pwmSetDuty(const uint16_t duty_cycle) {}
+ void libServo::pauseSoftPWM() {}
+
+#endif
+
+#endif // HAS_SERVOS
+
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/Servo.h b/Marlin/src/HAL/STM32F1/Servo.h
new file mode 100644
index 0000000000..745a1c93f0
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/Servo.h
@@ -0,0 +1,61 @@
+/**
+ * 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
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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
+
+// Pin number of unattached pins
+#define NOT_ATTACHED (-1)
+#define INVALID_SERVO 255
+
+#ifndef MAX_SERVOS
+ #define MAX_SERVOS 3
+#endif
+
+#define SERVO_DEFAULT_MIN_PW 544
+#define SERVO_DEFAULT_MAX_PW 2400
+#define SERVO_DEFAULT_MIN_ANGLE 0
+#define SERVO_DEFAULT_MAX_ANGLE 180
+
+class libServo;
+typedef libServo hal_servo_t;
+
+class libServo {
+ public:
+ libServo();
+ bool attach(const int32_t pin, const int32_t minAngle=SERVO_DEFAULT_MIN_ANGLE, const int32_t maxAngle=SERVO_DEFAULT_MAX_ANGLE);
+ bool attached() const { return pin != NOT_ATTACHED; }
+ bool detach();
+ void move(const int32_t value);
+ int32_t read() const;
+ private:
+ void servoWrite(uint8_t pin, const uint16_t duty_cycle);
+
+ uint8_t servoIndex; // index into the channel data for this servo
+ int32_t pin = NOT_ATTACHED;
+ int32_t minAngle;
+ int32_t maxAngle;
+ int32_t angle;
+
+ bool setupSoftPWM(const int32_t pin);
+ void pauseSoftPWM();
+ void pwmSetDuty(const uint16_t duty_cycle);
+};
diff --git a/Marlin/src/HAL/STM32F1/build_flags.py b/Marlin/src/HAL/STM32F1/build_flags.py
new file mode 100644
index 0000000000..970ca8b767
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/build_flags.py
@@ -0,0 +1,56 @@
+from __future__ import print_function
+import sys
+
+#dynamic build flags for generic compile options
+if __name__ == "__main__":
+ args = " ".join([ "-std=gnu++14",
+ "-Os",
+ "-mcpu=cortex-m3",
+ "-mthumb",
+
+ "-fsigned-char",
+ "-fno-move-loop-invariants",
+ "-fno-strict-aliasing",
+ "-fsingle-precision-constant",
+
+ "--specs=nano.specs",
+ "--specs=nosys.specs",
+
+ "-IMarlin/src/HAL/STM32F1",
+
+ "-MMD",
+ "-MP",
+ "-DTARGET_STM32F1"
+ ])
+
+ for i in range(1, len(sys.argv)):
+ args += " " + sys.argv[i]
+
+ print(args)
+
+# extra script for linker options
+else:
+ import pioutil
+ if pioutil.is_pio_build():
+ from SCons.Script import DefaultEnvironment
+ env = DefaultEnvironment()
+ env.Append(
+ ARFLAGS=["rcs"],
+
+ ASFLAGS=["-x", "assembler-with-cpp"],
+
+ CXXFLAGS=[
+ "-fabi-version=0",
+ "-fno-use-cxa-atexit",
+ "-fno-threadsafe-statics"
+ ],
+ LINKFLAGS=[
+ "-Os",
+ "-mcpu=cortex-m3",
+ "-ffreestanding",
+ "-mthumb",
+ "--specs=nano.specs",
+ "--specs=nosys.specs",
+ "-u_printf_float",
+ ],
+ )
diff --git a/Marlin/src/HAL/STM32F1/dogm/u8g_com_stm32duino_swspi.cpp b/Marlin/src/HAL/STM32F1/dogm/u8g_com_stm32duino_swspi.cpp
new file mode 100644
index 0000000000..26ea1ea19a
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/dogm/u8g_com_stm32duino_swspi.cpp
@@ -0,0 +1,170 @@
+/**
+ * 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 .
+ *
+ */
+
+#ifdef __STM32F1__
+
+#include "../../../inc/MarlinConfig.h"
+
+#if BOTH(HAS_MARLINUI_U8GLIB, FORCE_SOFT_SPI)
+
+#include
+#include "../../shared/HAL_SPI.h"
+
+#ifndef LCD_SPI_SPEED
+ #define LCD_SPI_SPEED SPI_FULL_SPEED // Fastest
+ //#define LCD_SPI_SPEED SPI_QUARTER_SPEED // Slower
+#endif
+
+static uint8_t SPI_speed = LCD_SPI_SPEED;
+
+static inline uint8_t swSpiTransfer_mode_0(uint8_t b, const uint8_t spi_speed, const pin_t miso_pin=-1) {
+ LOOP_L_N(i, 8) {
+ if (spi_speed == 0) {
+ WRITE(DOGLCD_MOSI, !!(b & 0x80));
+ WRITE(DOGLCD_SCK, HIGH);
+ b <<= 1;
+ if (miso_pin >= 0 && READ(miso_pin)) b |= 1;
+ WRITE(DOGLCD_SCK, LOW);
+ }
+ else {
+ const uint8_t state = (b & 0x80) ? HIGH : LOW;
+ LOOP_L_N(j, spi_speed)
+ WRITE(DOGLCD_MOSI, state);
+
+ LOOP_L_N(j, spi_speed + (miso_pin >= 0 ? 0 : 1))
+ WRITE(DOGLCD_SCK, HIGH);
+
+ b <<= 1;
+ if (miso_pin >= 0 && READ(miso_pin)) b |= 1;
+
+ LOOP_L_N(j, spi_speed)
+ WRITE(DOGLCD_SCK, LOW);
+ }
+ }
+ return b;
+}
+
+static inline uint8_t swSpiTransfer_mode_3(uint8_t b, const uint8_t spi_speed, const pin_t miso_pin=-1) {
+ LOOP_L_N(i, 8) {
+ const uint8_t state = (b & 0x80) ? HIGH : LOW;
+ if (spi_speed == 0) {
+ WRITE(DOGLCD_SCK, LOW);
+ WRITE(DOGLCD_MOSI, state);
+ WRITE(DOGLCD_MOSI, state); // need some setup time
+ WRITE(DOGLCD_SCK, HIGH);
+ }
+ else {
+ LOOP_L_N(j, spi_speed + (miso_pin >= 0 ? 0 : 1))
+ WRITE(DOGLCD_SCK, LOW);
+
+ LOOP_L_N(j, spi_speed)
+ WRITE(DOGLCD_MOSI, state);
+
+ LOOP_L_N(j, spi_speed)
+ WRITE(DOGLCD_SCK, HIGH);
+ }
+ b <<= 1;
+ if (miso_pin >= 0 && READ(miso_pin)) b |= 1;
+ }
+ return b;
+}
+
+static void u8g_sw_spi_HAL_STM32F1_shift_out(uint8_t val) {
+ #if ENABLED(FYSETC_MINI_12864)
+ swSpiTransfer_mode_3(val, SPI_speed);
+ #else
+ swSpiTransfer_mode_0(val, SPI_speed);
+ #endif
+}
+
+static uint8_t swSpiInit(const uint8_t spi_speed) {
+ #if PIN_EXISTS(LCD_RESET)
+ SET_OUTPUT(LCD_RESET_PIN);
+ #endif
+ SET_OUTPUT(DOGLCD_A0);
+ OUT_WRITE(DOGLCD_SCK, LOW);
+ OUT_WRITE(DOGLCD_MOSI, LOW);
+ OUT_WRITE(DOGLCD_CS, HIGH);
+ return spi_speed;
+}
+
+uint8_t u8g_com_HAL_STM32F1_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) {
+ switch (msg) {
+ case U8G_COM_MSG_INIT:
+ SPI_speed = swSpiInit(LCD_SPI_SPEED);
+ break;
+
+ case U8G_COM_MSG_STOP:
+ break;
+
+ case U8G_COM_MSG_RESET:
+ #if PIN_EXISTS(LCD_RESET)
+ WRITE(LCD_RESET_PIN, arg_val);
+ #endif
+ break;
+
+ case U8G_COM_MSG_CHIP_SELECT:
+ #if ENABLED(FYSETC_MINI_12864) // This LCD SPI is running mode 3 while SD card is running mode 0
+ if (arg_val) { // SCK idle state needs to be set to the proper idle state before
+ // the next chip select goes active
+ WRITE(DOGLCD_SCK, HIGH); // Set SCK to mode 3 idle state before CS goes active
+ WRITE(DOGLCD_CS, LOW);
+ }
+ else {
+ WRITE(DOGLCD_CS, HIGH);
+ WRITE(DOGLCD_SCK, LOW); // Set SCK to mode 0 idle state after CS goes inactive
+ }
+ #else
+ WRITE(DOGLCD_CS, !arg_val);
+ #endif
+ break;
+
+ case U8G_COM_MSG_WRITE_BYTE:
+ u8g_sw_spi_HAL_STM32F1_shift_out(arg_val);
+ break;
+
+ case U8G_COM_MSG_WRITE_SEQ: {
+ uint8_t *ptr = (uint8_t *)arg_ptr;
+ while (arg_val > 0) {
+ u8g_sw_spi_HAL_STM32F1_shift_out(*ptr++);
+ arg_val--;
+ }
+ } break;
+
+ case U8G_COM_MSG_WRITE_SEQ_P: {
+ uint8_t *ptr = (uint8_t *)arg_ptr;
+ while (arg_val > 0) {
+ u8g_sw_spi_HAL_STM32F1_shift_out(u8g_pgm_read(ptr));
+ ptr++;
+ arg_val--;
+ }
+ } break;
+
+ case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
+ WRITE(DOGLCD_A0, arg_val);
+ break;
+ }
+ return 1;
+}
+
+#endif // HAS_MARLINUI_U8GLIB && FORCE_SOFT_SPI
+#endif // STM32F1
diff --git a/Marlin/src/HAL/STM32F1/eeprom_bl24cxx.cpp b/Marlin/src/HAL/STM32F1/eeprom_bl24cxx.cpp
new file mode 100644
index 0000000000..4e25bc69da
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/eeprom_bl24cxx.cpp
@@ -0,0 +1,82 @@
+/**
+ * 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 .
+ *
+ */
+#ifdef __STM32F1__
+
+/**
+ * PersistentStore for Arduino-style EEPROM interface
+ * with simple implementations supplied by Marlin.
+ */
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(IIC_BL24CXX_EEPROM)
+
+#include "../shared/eeprom_if.h"
+#include "../shared/eeprom_api.h"
+
+//
+// PersistentStore
+//
+
+#ifndef MARLIN_EEPROM_SIZE
+ #error "MARLIN_EEPROM_SIZE is required for IIC_BL24CXX_EEPROM."
+#endif
+
+size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; }
+
+bool PersistentStore::access_start() { eeprom_init(); return true; }
+bool PersistentStore::access_finish() { return true; }
+
+bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+ uint16_t written = 0;
+ while (size--) {
+ uint8_t v = *value;
+ uint8_t * const p = (uint8_t * const)pos;
+ if (v != eeprom_read_byte(p)) { // EEPROM has only ~100,000 write cycles, so only write bytes that have changed!
+ eeprom_write_byte(p, v);
+ if (++written & 0x7F) delay(2); else safe_delay(2); // Avoid triggering watchdog during long EEPROM writes
+ if (eeprom_read_byte(p) != v) {
+ SERIAL_ECHO_MSG(STR_ERR_EEPROM_WRITE);
+ return true;
+ }
+ }
+ crc16(crc, &v, 1);
+ pos++;
+ value++;
+ }
+ return false;
+}
+
+bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
+ do {
+ uint8_t * const p = (uint8_t * const)pos;
+ uint8_t c = eeprom_read_byte(p);
+ if (writing) *value = c;
+ crc16(crc, &c, 1);
+ pos++;
+ value++;
+ } while (--size);
+ return false;
+}
+
+#endif // IIC_BL24CXX_EEPROM
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/eeprom_flash.cpp b/Marlin/src/HAL/STM32F1/eeprom_flash.cpp
new file mode 100644
index 0000000000..e7d9dd29e2
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/eeprom_flash.cpp
@@ -0,0 +1,113 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
+ * Copyright (c) 2016 Victor Perez victor_pv@hotmail.com
+ *
+ * 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 .
+ *
+ */
+
+/**
+ * persistent_store_flash.cpp
+ * HAL for stm32duino and compatible (STM32F1)
+ * Implementation of EEPROM settings in SDCard
+ */
+
+#ifdef __STM32F1__
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(FLASH_EEPROM_EMULATION)
+
+#include "../shared/eeprom_api.h"
+
+#include
+#include
+
+// Store settings in the last two pages
+#ifndef MARLIN_EEPROM_SIZE
+ #define MARLIN_EEPROM_SIZE ((EEPROM_PAGE_SIZE) * 2)
+#endif
+size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; }
+
+static uint8_t ram_eeprom[MARLIN_EEPROM_SIZE] __attribute__((aligned(4))) = {0};
+static bool eeprom_dirty = false;
+
+bool PersistentStore::access_start() {
+ const uint32_t *source = reinterpret_cast(EEPROM_PAGE0_BASE);
+ uint32_t *destination = reinterpret_cast(ram_eeprom);
+
+ static_assert(0 == (MARLIN_EEPROM_SIZE) % 4, "MARLIN_EEPROM_SIZE is corrupted. (Must be a multiple of 4.)"); // Ensure copying as uint32_t is safe
+ constexpr size_t eeprom_size_u32 = (MARLIN_EEPROM_SIZE) / 4;
+
+ for (size_t i = 0; i < eeprom_size_u32; ++i, ++destination, ++source)
+ *destination = *source;
+
+ eeprom_dirty = false;
+ return true;
+}
+
+bool PersistentStore::access_finish() {
+
+ if (eeprom_dirty) {
+ FLASH_Status status;
+
+ // Instead of erasing all (both) pages, maybe in the loop we check what page we are in, and if the
+ // data has changed in that page. We then erase the first time we "detect" a change. In theory, if
+ // nothing changed in a page, we wouldn't need to erase/write it.
+ // Or, instead of checking at this point, turn eeprom_dirty into an array of bool the size of number
+ // of pages. Inside write_data, we set the flag to true at that time if something in that
+ // page changes...either way, something to look at later.
+ FLASH_Unlock();
+
+ #define ACCESS_FINISHED(TF) { FLASH_Lock(); eeprom_dirty = false; return TF; }
+
+ status = FLASH_ErasePage(EEPROM_PAGE0_BASE);
+ if (status != FLASH_COMPLETE) ACCESS_FINISHED(true);
+ status = FLASH_ErasePage(EEPROM_PAGE1_BASE);
+ if (status != FLASH_COMPLETE) ACCESS_FINISHED(true);
+
+ const uint16_t *source = reinterpret_cast(ram_eeprom);
+ for (size_t i = 0; i < MARLIN_EEPROM_SIZE; i += 2, ++source) {
+ if (FLASH_ProgramHalfWord(EEPROM_PAGE0_BASE + i, *source) != FLASH_COMPLETE)
+ ACCESS_FINISHED(false);
+ }
+
+ ACCESS_FINISHED(true);
+ }
+
+ return true;
+}
+
+bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+ for (size_t i = 0; i < size; ++i) ram_eeprom[pos + i] = value[i];
+ eeprom_dirty = true;
+ crc16(crc, value, size);
+ pos += size;
+ return false; // return true for any error
+}
+
+bool PersistentStore::read_data(int &pos, uint8_t *value, const size_t size, uint16_t *crc, const bool writing/*=true*/) {
+ const uint8_t * const buff = writing ? &value[0] : &ram_eeprom[pos];
+ if (writing) for (size_t i = 0; i < size; i++) value[i] = ram_eeprom[pos + i];
+ crc16(crc, buff, size);
+ pos += size;
+ return false; // return true for any error
+}
+
+#endif // FLASH_EEPROM_EMULATION
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/eeprom_if_iic.cpp b/Marlin/src/HAL/STM32F1/eeprom_if_iic.cpp
new file mode 100644
index 0000000000..78b7af0b04
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/eeprom_if_iic.cpp
@@ -0,0 +1,54 @@
+/**
+ * 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 .
+ *
+ */
+
+/**
+ * Platform-independent Arduino functions for I2C EEPROM.
+ * Enable USE_SHARED_EEPROM if not supplied by the framework.
+ */
+
+#ifdef __STM32F1__
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(IIC_BL24CXX_EEPROM)
+
+#include "../../libs/BL24CXX.h"
+#include "../shared/eeprom_if.h"
+
+void eeprom_init() { BL24CXX::init(); }
+
+// ------------------------
+// Public functions
+// ------------------------
+
+void eeprom_write_byte(uint8_t *pos, uint8_t value) {
+ const unsigned eeprom_address = (unsigned)pos;
+ return BL24CXX::writeOneByte(eeprom_address, value);
+}
+
+uint8_t eeprom_read_byte(uint8_t *pos) {
+ const unsigned eeprom_address = (unsigned)pos;
+ return BL24CXX::readOneByte(eeprom_address);
+}
+
+#endif // IIC_BL24CXX_EEPROM
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/eeprom_sdcard.cpp b/Marlin/src/HAL/STM32F1/eeprom_sdcard.cpp
new file mode 100644
index 0000000000..9cfa97c1ab
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/eeprom_sdcard.cpp
@@ -0,0 +1,93 @@
+/**
+ * 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 .
+ *
+ */
+
+/**
+ * HAL for stm32duino.com based on Libmaple and compatible (STM32F1)
+ * Implementation of EEPROM settings in SD Card
+ */
+
+#ifdef __STM32F1__
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(SDCARD_EEPROM_EMULATION)
+
+#include "../shared/eeprom_api.h"
+#include "../../sd/cardreader.h"
+
+#define EEPROM_FILENAME "eeprom.dat"
+
+#ifndef MARLIN_EEPROM_SIZE
+ #define MARLIN_EEPROM_SIZE 0x1000 // 4KB
+#endif
+size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; }
+
+#define _ALIGN(x) __attribute__ ((aligned(x))) // SDIO uint32_t* compat.
+static char _ALIGN(4) HAL_eeprom_data[MARLIN_EEPROM_SIZE];
+
+bool PersistentStore::access_start() {
+ if (!card.isMounted()) return false;
+
+ MediaFile file, root = card.getroot();
+ if (!file.open(&root, EEPROM_FILENAME, O_RDONLY))
+ return true; // false aborts the save
+
+ int bytes_read = file.read(HAL_eeprom_data, MARLIN_EEPROM_SIZE);
+ if (bytes_read < 0) return false;
+ for (; bytes_read < MARLIN_EEPROM_SIZE; bytes_read++)
+ HAL_eeprom_data[bytes_read] = 0xFF;
+ file.close();
+ return true;
+}
+
+bool PersistentStore::access_finish() {
+ if (!card.isMounted()) return false;
+
+ MediaFile file, root = card.getroot();
+ int bytes_written = 0;
+ if (file.open(&root, EEPROM_FILENAME, O_CREAT | O_WRITE | O_TRUNC)) {
+ bytes_written = file.write(HAL_eeprom_data, MARLIN_EEPROM_SIZE);
+ file.close();
+ }
+ return (bytes_written == MARLIN_EEPROM_SIZE);
+}
+
+bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+ for (size_t i = 0; i < size; i++)
+ HAL_eeprom_data[pos + i] = value[i];
+ crc16(crc, value, size);
+ pos += size;
+ return false;
+}
+
+bool PersistentStore::read_data(int &pos, uint8_t *value, const size_t size, uint16_t *crc, const bool writing/*=true*/) {
+ for (size_t i = 0; i < size; i++) {
+ uint8_t c = HAL_eeprom_data[pos + i];
+ if (writing) value[i] = c;
+ crc16(crc, &c, 1);
+ }
+ pos += size;
+ return false;
+}
+
+#endif // SDCARD_EEPROM_EMULATION
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/eeprom_wired.cpp b/Marlin/src/HAL/STM32F1/eeprom_wired.cpp
new file mode 100644
index 0000000000..bc48eef34f
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/eeprom_wired.cpp
@@ -0,0 +1,89 @@
+/**
+ * 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 .
+ *
+ */
+
+/**
+ * HAL PersistentStore for STM32F1
+ */
+
+#ifdef __STM32F1__
+
+#include "../../inc/MarlinConfig.h"
+
+#if USE_WIRED_EEPROM
+
+#include "../shared/eeprom_if.h"
+#include "../shared/eeprom_api.h"
+
+#ifndef MARLIN_EEPROM_SIZE
+ #error "MARLIN_EEPROM_SIZE is required for I2C / SPI EEPROM."
+#endif
+size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; }
+
+bool PersistentStore::access_finish() { return true; }
+
+bool PersistentStore::access_start() {
+ eeprom_init();
+ #if ENABLED(SPI_EEPROM)
+ #if SPI_CHAN_EEPROM1 == 1
+ SET_OUTPUT(BOARD_SPI1_SCK_PIN);
+ SET_OUTPUT(BOARD_SPI1_MOSI_PIN);
+ SET_INPUT(BOARD_SPI1_MISO_PIN);
+ SET_OUTPUT(SPI_EEPROM1_CS_PIN);
+ #endif
+ spiInit(0);
+ #endif
+ return true;
+}
+
+bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+ uint16_t written = 0;
+ while (size--) {
+ uint8_t * const p = (uint8_t * const)pos;
+ uint8_t v = *value;
+ if (v != eeprom_read_byte(p)) { // EEPROM has only ~100,000 write cycles, so only write bytes that have changed!
+ eeprom_write_byte(p, v);
+ if (++written & 0x7F) delay(2); else safe_delay(2); // Avoid triggering watchdog during long EEPROM writes
+ if (eeprom_read_byte(p) != v) {
+ SERIAL_ECHO_MSG(STR_ERR_EEPROM_WRITE);
+ return true;
+ }
+ }
+ crc16(crc, &v, 1);
+ pos++;
+ value++;
+ }
+ return false;
+}
+
+bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
+ do {
+ uint8_t c = eeprom_read_byte((uint8_t*)pos);
+ if (writing && value) *value = c;
+ crc16(crc, &c, 1);
+ pos++;
+ value++;
+ } while (--size);
+ return false;
+}
+
+#endif // USE_WIRED_EEPROM
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/endstop_interrupts.h b/Marlin/src/HAL/STM32F1/endstop_interrupts.h
new file mode 100644
index 0000000000..a1ef8a8c3a
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/endstop_interrupts.h
@@ -0,0 +1,86 @@
+/**
+ * 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
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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
+
+/**
+ * Endstop interrupts for Libmaple STM32F1 based targets.
+ *
+ * On STM32F, all pins support external interrupt capability.
+ * Any pin can be used for external interrupts, but there are some restrictions.
+ * At most 16 different external interrupts can be used at one time.
+ * Further, you can’t just pick any 16 pins to use. This is because every pin on the STM32
+ * connects to what is called an EXTI line, and only one pin per EXTI line can be used for external interrupts at a time
+ * Check the Reference Manual of the MCU to confirm which line is used by each pin
+ */
+
+/**
+ * Endstop Interrupts
+ *
+ * Without endstop interrupts the endstop pins must be polled continually in
+ * the temperature-ISR via endstops.update(), most of the time finding no change.
+ * With this feature endstops.update() is called only when we know that at
+ * least one endstop has changed state, saving valuable CPU cycles.
+ *
+ * This feature only works when all used endstop pins can generate an 'external interrupt'.
+ *
+ * Test whether pins issue interrupts on your board by flashing 'pin_interrupt_test.ino'.
+ * (Located in Marlin/buildroot/share/pin_interrupt_test/pin_interrupt_test.ino)
+ */
+
+#include "../../module/endstops.h"
+
+// One ISR for all EXT-Interrupts
+void endstop_ISR() { endstops.update(); }
+
+void setup_endstop_interrupts() {
+ #define _ATTACH(P) attachInterrupt(P, endstop_ISR, CHANGE)
+ TERN_(HAS_X_MAX, _ATTACH(X_MAX_PIN));
+ TERN_(HAS_X_MIN, _ATTACH(X_MIN_PIN));
+ TERN_(HAS_Y_MAX, _ATTACH(Y_MAX_PIN));
+ TERN_(HAS_Y_MIN, _ATTACH(Y_MIN_PIN));
+ TERN_(HAS_Z_MAX, _ATTACH(Z_MAX_PIN));
+ TERN_(HAS_Z_MIN, _ATTACH(Z_MIN_PIN));
+ TERN_(HAS_X2_MAX, _ATTACH(X2_MAX_PIN));
+ TERN_(HAS_X2_MIN, _ATTACH(X2_MIN_PIN));
+ TERN_(HAS_Y2_MAX, _ATTACH(Y2_MAX_PIN));
+ TERN_(HAS_Y2_MIN, _ATTACH(Y2_MIN_PIN));
+ TERN_(HAS_Z2_MAX, _ATTACH(Z2_MAX_PIN));
+ TERN_(HAS_Z2_MIN, _ATTACH(Z2_MIN_PIN));
+ TERN_(HAS_Z3_MAX, _ATTACH(Z3_MAX_PIN));
+ TERN_(HAS_Z3_MIN, _ATTACH(Z3_MIN_PIN));
+ TERN_(HAS_Z4_MAX, _ATTACH(Z4_MAX_PIN));
+ TERN_(HAS_Z4_MIN, _ATTACH(Z4_MIN_PIN));
+ TERN_(HAS_Z_MIN_PROBE_PIN, _ATTACH(Z_MIN_PROBE_PIN));
+ TERN_(HAS_I_MAX, _ATTACH(I_MAX_PIN));
+ TERN_(HAS_I_MIN, _ATTACH(I_MIN_PIN));
+ TERN_(HAS_J_MAX, _ATTACH(J_MAX_PIN));
+ TERN_(HAS_J_MIN, _ATTACH(J_MIN_PIN));
+ TERN_(HAS_K_MAX, _ATTACH(K_MAX_PIN));
+ TERN_(HAS_K_MIN, _ATTACH(K_MIN_PIN));
+ TERN_(HAS_U_MAX, _ATTACH(U_MAX_PIN));
+ TERN_(HAS_U_MIN, _ATTACH(U_MIN_PIN));
+ TERN_(HAS_V_MAX, _ATTACH(V_MAX_PIN));
+ TERN_(HAS_V_MIN, _ATTACH(V_MIN_PIN));
+ TERN_(HAS_W_MAX, _ATTACH(W_MAX_PIN));
+ TERN_(HAS_W_MIN, _ATTACH(W_MIN_PIN));
+}
diff --git a/Marlin/src/HAL/STM32F1/fast_pwm.cpp b/Marlin/src/HAL/STM32F1/fast_pwm.cpp
new file mode 100644
index 0000000000..c3f96f0f92
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/fast_pwm.cpp
@@ -0,0 +1,85 @@
+/**
+ * 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 .
+ *
+ */
+#ifdef __STM32F1__
+
+#include "../../inc/MarlinConfig.h"
+
+#include
+
+#define NR_TIMERS TERN(STM32_XL_DENSITY, 14, 8) // Maple timers, 14 for STM32_XL_DENSITY (F/G chips), 8 for HIGH density (C D E)
+
+static uint16_t timer_freq[NR_TIMERS];
+
+inline uint8_t timer_and_index_for_pin(const pin_t pin, timer_dev **timer_ptr) {
+ *timer_ptr = PIN_MAP[pin].timer_device;
+ for (uint8_t i = 0; i < NR_TIMERS; i++) if (*timer_ptr == HAL_get_timer_dev(i))
+ return i;
+ return 0;
+}
+
+void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) {
+ const uint16_t duty = invert ? v_size - v : v;
+ if (PWM_PIN(pin)) {
+ timer_dev *timer;
+ if (timer_freq[timer_and_index_for_pin(pin, &timer)] == 0)
+ set_pwm_frequency(pin, PWM_FREQUENCY);
+ const uint8_t channel = PIN_MAP[pin].timer_channel;
+ timer_set_compare(timer, channel, duty);
+ timer_set_mode(timer, channel, TIMER_PWM); // PWM Output Mode
+ }
+ else {
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, duty < v_size / 2 ? LOW : HIGH);
+ }
+}
+
+void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) {
+ if (!PWM_PIN(pin)) return; // Don't proceed if no hardware timer
+
+ timer_dev *timer;
+ timer_freq[timer_and_index_for_pin(pin, &timer)] = f_desired;
+
+ // Protect used timers
+ if (timer == HAL_get_timer_dev(MF_TIMER_TEMP)) return;
+ if (timer == HAL_get_timer_dev(MF_TIMER_STEP)) return;
+ #if MF_TIMER_PULSE != MF_TIMER_STEP
+ if (timer == HAL_get_timer_dev(MF_TIMER_PULSE)) return;
+ #endif
+
+ if (!(timer->regs.bas->SR & TIMER_CR1_CEN)) // Ensure the timer is enabled
+ timer_init(timer);
+
+ const uint8_t channel = PIN_MAP[pin].timer_channel;
+ timer_set_mode(timer, channel, TIMER_PWM);
+ // Preload (resolution) cannot be equal to duty of 255 otherwise it may not result in digital off or on.
+ uint16_t preload = 254;
+ int32_t prescaler = (HAL_TIMER_RATE) / (preload + 1) / f_desired - 1;
+ if (prescaler > 65535) { // For low frequencies increase prescaler
+ prescaler = 65535;
+ preload = (HAL_TIMER_RATE) / (prescaler + 1) / f_desired - 1;
+ }
+ if (prescaler < 0) return; // Too high frequency
+ timer_set_reload(timer, preload);
+ timer_set_prescaler(timer, prescaler);
+}
+
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/fastio.h b/Marlin/src/HAL/STM32F1/fastio.h
new file mode 100644
index 0000000000..e75254d692
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/fastio.h
@@ -0,0 +1,186 @@
+/**
+ * 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
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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
+
+/**
+ * Fast I/O interfaces for STM32F1
+ * These use GPIO functions instead of Direct Port Manipulation, as on AVR.
+ */
+
+#include
+
+#define READ(IO) (PIN_MAP[IO].gpio_device->regs->IDR & _BV32(PIN_MAP[IO].gpio_bit) ? HIGH : LOW)
+#define WRITE(IO,V) (PIN_MAP[IO].gpio_device->regs->BSRR = _BV32(PIN_MAP[IO].gpio_bit) << ((V) ? 0 : 16))
+#define TOGGLE(IO) TBI32(PIN_MAP[IO].gpio_device->regs->ODR, PIN_MAP[IO].gpio_bit)
+
+#define _GET_MODE(IO) gpio_get_mode(PIN_MAP[IO].gpio_device, PIN_MAP[IO].gpio_bit)
+#define _SET_MODE(IO,M) gpio_set_mode(PIN_MAP[IO].gpio_device, PIN_MAP[IO].gpio_bit, M)
+#define _SET_OUTPUT(IO) _SET_MODE(IO, GPIO_OUTPUT_PP)
+#define _SET_OUTPUT_OD(IO) _SET_MODE(IO, GPIO_OUTPUT_OD)
+
+#define OUT_WRITE(IO,V) do{ _SET_OUTPUT(IO); WRITE(IO,V); }while(0)
+#define OUT_WRITE_OD(IO,V) do{ _SET_OUTPUT_OD(IO); WRITE(IO,V); }while(0)
+
+#define SET_INPUT(IO) _SET_MODE(IO, GPIO_INPUT_FLOATING)
+#define SET_INPUT_PULLUP(IO) _SET_MODE(IO, GPIO_INPUT_PU)
+#define SET_INPUT_PULLDOWN(IO) _SET_MODE(IO, GPIO_INPUT_PD)
+#define SET_OUTPUT(IO) OUT_WRITE(IO, LOW)
+#define SET_PWM(IO) pinMode(IO, PWM) // do{ gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, GPIO_AF_OUTPUT_PP); timer_set_mode(PIN_MAP[pin].timer_device, PIN_MAP[pin].timer_channel, TIMER_PWM); }while(0)
+#define SET_PWM_OD(IO) pinMode(IO, PWM_OPEN_DRAIN)
+
+#define IS_INPUT(IO) (_GET_MODE(IO) == GPIO_INPUT_FLOATING || _GET_MODE(IO) == GPIO_INPUT_ANALOG || _GET_MODE(IO) == GPIO_INPUT_PU || _GET_MODE(IO) == GPIO_INPUT_PD)
+#define IS_OUTPUT(IO) (_GET_MODE(IO) == GPIO_OUTPUT_PP || _GET_MODE(IO) == GPIO_OUTPUT_OD)
+
+#define PWM_PIN(IO) !!PIN_MAP[IO].timer_device
+
+// digitalRead/Write wrappers
+#define extDigitalRead(IO) digitalRead(IO)
+#define extDigitalWrite(IO,V) digitalWrite(IO,V)
+
+//
+// Pins Definitions
+//
+#define PA0 0x00
+#define PA1 0x01
+#define PA2 0x02
+#define PA3 0x03
+#define PA4 0x04
+#define PA5 0x05
+#define PA6 0x06
+#define PA7 0x07
+#define PA8 0x08
+#define PA9 0x09
+#define PA10 0x0A
+#define PA11 0x0B
+#define PA12 0x0C
+#define PA13 0x0D
+#define PA14 0x0E
+#define PA15 0x0F
+
+#define PB0 0x10
+#define PB1 0x11
+#define PB2 0x12
+#define PB3 0x13
+#define PB4 0x14
+#define PB5 0x15
+#define PB6 0x16
+#define PB7 0x17 // 36 pins (F103T)
+#define PB8 0x18
+#define PB9 0x19
+#define PB10 0x1A
+#define PB11 0x1B
+#define PB12 0x1C
+#define PB13 0x1D
+#define PB14 0x1E
+#define PB15 0x1F
+
+#if defined(MCU_STM32F103CB) || defined(MCU_STM32F103C8)
+ #define PC13 0x20
+ #define PC14 0x21
+ #define PC15 0x22
+#else
+ #define PC0 0x20
+ #define PC1 0x21
+ #define PC2 0x22
+ #define PC3 0x23
+ #define PC4 0x24
+ #define PC5 0x25
+ #define PC6 0x26
+ #define PC7 0x27
+ #define PC8 0x28
+ #define PC9 0x29
+ #define PC10 0x2A
+ #define PC11 0x2B
+ #define PC12 0x2C
+ #define PC13 0x2D
+ #define PC14 0x2E
+ #define PC15 0x2F
+#endif
+
+#define PD0 0x30
+#define PD1 0x31
+#define PD2 0x32 // 64 pins (F103R)
+#define PD3 0x33
+#define PD4 0x34
+#define PD5 0x35
+#define PD6 0x36
+#define PD7 0x37
+#define PD8 0x38
+#define PD9 0x39
+#define PD10 0x3A
+#define PD11 0x3B
+#define PD12 0x3C
+#define PD13 0x3D
+#define PD14 0x3E
+#define PD15 0x3F
+
+#define PE0 0x40
+#define PE1 0x41
+#define PE2 0x42
+#define PE3 0x43
+#define PE4 0x44
+#define PE5 0x45
+#define PE6 0x46
+#define PE7 0x47
+#define PE8 0x48
+#define PE9 0x49
+#define PE10 0x4A
+#define PE11 0x4B
+#define PE12 0x4C
+#define PE13 0x4D
+#define PE14 0x4E
+#define PE15 0x4F // 100 pins (F103V)
+
+#define PF0 0x50
+#define PF1 0x51
+#define PF2 0x52
+#define PF3 0x53
+#define PF4 0x54
+#define PF5 0x55
+#define PF6 0x56
+#define PF7 0x57
+#define PF8 0x58
+#define PF9 0x59
+#define PF10 0x5A
+#define PF11 0x5B
+#define PF12 0x5C
+#define PF13 0x5D
+#define PF14 0x5E
+#define PF15 0x5F
+
+#define PG0 0x60
+#define PG1 0x61
+#define PG2 0x62
+#define PG3 0x63
+#define PG4 0x64
+#define PG5 0x65
+#define PG6 0x66
+#define PG7 0x67
+#define PG8 0x68
+#define PG9 0x69
+#define PG10 0x6A
+#define PG11 0x6B
+#define PG12 0x6C
+#define PG13 0x6D
+#define PG14 0x6E
+#define PG15 0x6F // 144 pins (F103Z)
diff --git a/Marlin/src/HAL/STM32F1/inc/Conditionals_LCD.h b/Marlin/src/HAL/STM32F1/inc/Conditionals_LCD.h
new file mode 100644
index 0000000000..5f1c4b1601
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/inc/Conditionals_LCD.h
@@ -0,0 +1,22 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
diff --git a/Marlin/src/HAL/STM32F1/inc/Conditionals_adv.h b/Marlin/src/HAL/STM32F1/inc/Conditionals_adv.h
new file mode 100644
index 0000000000..0fe7924765
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/inc/Conditionals_adv.h
@@ -0,0 +1,30 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#ifdef USE_USB_COMPOSITE
+ //#warning "SD_CHECK_AND_RETRY isn't needed with USE_USB_COMPOSITE."
+ #undef SD_CHECK_AND_RETRY
+ #if DISABLED(NO_SD_HOST_DRIVE)
+ #define HAS_SD_HOST_DRIVE 1
+ #endif
+#endif
diff --git a/Marlin/src/HAL/STM32F1/inc/Conditionals_post.h b/Marlin/src/HAL/STM32F1/inc/Conditionals_post.h
new file mode 100644
index 0000000000..656fbe1ce2
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/inc/Conditionals_post.h
@@ -0,0 +1,34 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+// If no real EEPROM, Flash emulation, or SRAM emulation is available fall back to SD emulation
+#if USE_FALLBACK_EEPROM
+ #define SDCARD_EEPROM_EMULATION
+#elif EITHER(I2C_EEPROM, SPI_EEPROM)
+ #define USE_SHARED_EEPROM 1
+#endif
+
+// Allow SDSUPPORT to be disabled
+#if DISABLED(SDSUPPORT)
+ #undef SDIO_SUPPORT
+#endif
diff --git a/Marlin/src/HAL/STM32F1/inc/SanityCheck.h b/Marlin/src/HAL/STM32F1/inc/SanityCheck.h
new file mode 100644
index 0000000000..fe8f6e0ec2
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/inc/SanityCheck.h
@@ -0,0 +1,51 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+/**
+ * Test STM32F1-specific configuration values for errors at compile-time.
+ */
+
+#if ENABLED(SDCARD_EEPROM_EMULATION) && DISABLED(SDSUPPORT)
+ #undef SDCARD_EEPROM_EMULATION // Avoid additional error noise
+ #if USE_FALLBACK_EEPROM
+ #warning "EEPROM type not specified. Fallback is SDCARD_EEPROM_EMULATION."
+ #endif
+ #error "SDCARD_EEPROM_EMULATION requires SDSUPPORT. Enable SDSUPPORT or choose another EEPROM emulation."
+#endif
+
+#if ENABLED(SERIAL_STATS_MAX_RX_QUEUED)
+ #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on the STM32F1 platform."
+#elif ENABLED(SERIAL_STATS_DROPPED_RX)
+ #error "SERIAL_STATS_DROPPED_RX is not supported on the STM32F1 platform."
+#endif
+
+#if ENABLED(NEOPIXEL_LED) && DISABLED(FYSETC_MINI_12864_2_1)
+ #error "NEOPIXEL_LED (Adafruit NeoPixel) is not supported for HAL/STM32F1. Comment out this line to proceed at your own risk!"
+#endif
+
+// Emergency Parser needs at least one serial with HardwareSerial or USBComposite.
+// The USBSerial maple don't allow any hook to implement EMERGENCY_PARSER.
+// And copy all USBSerial code to marlin space to support EMERGENCY_PARSER, when we have another options, don't worth it.
+#if ENABLED(EMERGENCY_PARSER) && !defined(USE_USB_COMPOSITE) && ((SERIAL_PORT == -1 && !defined(SERIAL_PORT_2)) || (SERIAL_PORT_2 == -1 && !defined(SERIAL_PORT)))
+ #error "EMERGENCY_PARSER is only supported by HardwareSerial or USBComposite in HAL/STM32F1."
+#endif
diff --git a/Marlin/src/HAL/STM32F1/maple_win_usb_driver/maple_serial.inf b/Marlin/src/HAL/STM32F1/maple_win_usb_driver/maple_serial.inf
new file mode 100644
index 0000000000..c39f4ce0ed
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/maple_win_usb_driver/maple_serial.inf
@@ -0,0 +1,56 @@
+;
+; STMicroelectronics Communication Device Class driver installation file
+; (C)2006 Copyright STMicroelectronics
+;
+
+[Version]
+Signature="$Windows NT$"
+Class=Ports
+ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
+Provider=%STM%
+LayoutFile=layout.inf
+
+[Manufacturer]
+%MFGNAME%=VirComDevice,NT,NTamd64
+
+[DestinationDirs]
+DefaultDestDir = 12
+
+[VirComDevice.NT]
+%DESCRIPTION%=DriverInstall,USB\VID_1EAF&PID_0029&MI_01
+%DESCRIPTION%=DriverInstall,USB\VID_1EAF&PID_0029&MI_01
+
+[VirComDevice.NTamd64]
+%DESCRIPTION%=DriverInstall,USB\VID_1EAF&PID_0029&MI_01
+%DESCRIPTION%=DriverInstall,USB\VID_1EAF&PID_0029&MI_01
+
+[DriverInstall.NT]
+Include=mdmcpq.inf
+CopyFiles=FakeModemCopyFileSection
+AddReg=DriverInstall.NT.AddReg
+
+[DriverInstall.NT.AddReg]
+HKR,,DevLoader,,*ntkern
+HKR,,NTMPDriver,,usbser.sys
+HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"
+
+[DriverInstall.NT.Services]
+AddService=usbser, 0x00000002, DriverServiceInst
+
+[DriverServiceInst]
+DisplayName=%SERVICE%
+ServiceType=1
+StartType=3
+ErrorControl=1
+ServiceBinary=%12%\usbser.sys
+
+;------------------------------------------------------------------------------
+; String Definitions
+;------------------------------------------------------------------------------
+
+
+[Strings]
+STM = "LeafLabs"
+MFGNAME = "LeafLabs"
+DESCRIPTION = "Maple R3"
+SERVICE = "USB Virtual COM port"
diff --git a/Marlin/src/HAL/STM32F1/msc_sd.cpp b/Marlin/src/HAL/STM32F1/msc_sd.cpp
new file mode 100644
index 0000000000..f490c83ed8
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/msc_sd.cpp
@@ -0,0 +1,98 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2019 BigTreeTech [https://github.com/bigtreetech]
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#ifdef __STM32F1__
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if HAS_SD_HOST_DRIVE
+
+#include "msc_sd.h"
+#include "SPI.h"
+#include "usb_reg_map.h"
+
+#define PRODUCT_ID 0x29
+
+USBMassStorage MarlinMSC;
+Serial1Class MarlinCompositeSerial(true);
+
+#include "../../inc/MarlinConfig.h"
+
+#if SD_CONNECTION_IS(ONBOARD)
+
+ #include "onboard_sd.h"
+
+ static bool MSC_Write(const uint8_t *writebuff, uint32_t startSector, uint16_t numSectors) {
+ return (disk_write(0, writebuff, startSector, numSectors) == RES_OK);
+ }
+ static bool MSC_Read(uint8_t *readbuff, uint32_t startSector, uint16_t numSectors) {
+ return (disk_read(0, readbuff, startSector, numSectors) == RES_OK);
+ }
+
+#endif
+
+#if ENABLED(EMERGENCY_PARSER)
+
+ // The original callback is not called (no way to retrieve address).
+ // That callback detects a special STM32 reset sequence: this functionality is not essential
+ // as M997 achieves the same.
+ void my_rx_callback(unsigned int, void*) {
+ // max length of 16 is enough to contain all emergency commands
+ uint8 buf[16];
+
+ //rx is usbSerialPart.endpoints[2]
+ uint16 len = usb_get_ep_rx_count(usbSerialPart.endpoints[2].address);
+ uint32 total = composite_cdcacm_data_available();
+
+ if (len == 0 || total == 0 || !WITHIN(total, len, COUNT(buf)))
+ return;
+
+ // cannot get character by character due to bug in composite_cdcacm_peek_ex
+ len = composite_cdcacm_peek(buf, total);
+
+ for (uint32 i = 0; i < len; i++)
+ emergency_parser.update(MarlinCompositeSerial.emergency_state, buf[i+total-len]);
+ }
+
+#endif
+
+void MSC_SD_init() {
+ USBComposite.setProductId(PRODUCT_ID);
+ // Just set MarlinCompositeSerial enabled to true
+ // because when MarlinCompositeSerial.begin() is used in setup()
+ // it clears all USBComposite devices.
+ MarlinCompositeSerial.begin();
+ USBComposite.end();
+ USBComposite.clear();
+ // Set api and register mass storage
+ #if SD_CONNECTION_IS(ONBOARD)
+ uint32_t cardSize;
+ if (disk_initialize(0) == RES_OK) {
+ if (disk_ioctl(0, GET_SECTOR_COUNT, (void *)(&cardSize)) == RES_OK) {
+ MarlinMSC.setDriveData(0, cardSize, MSC_Read, MSC_Write);
+ MarlinMSC.registerComponent();
+ }
+ }
+ #endif
+ // Register composite Serial
+ MarlinCompositeSerial.registerComponent();
+ USBComposite.begin();
+ #if ENABLED(EMERGENCY_PARSER)
+ composite_cdcacm_set_hooks(USBHID_CDCACM_HOOK_RX, my_rx_callback);
+ #endif
+}
+
+#endif // HAS_SD_HOST_DRIVE
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/msc_sd.h b/Marlin/src/HAL/STM32F1/msc_sd.h
new file mode 100644
index 0000000000..f4636bdff7
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/msc_sd.h
@@ -0,0 +1,26 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2019 BigTreeTech [https://github.com/bigtreetech]
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#pragma once
+
+#include
+
+#include "../../inc/MarlinConfigPre.h"
+#include "../../core/serial_hook.h"
+
+extern USBMassStorage MarlinMSC;
+extern Serial1Class MarlinCompositeSerial;
+
+void MSC_SD_init();
diff --git a/Marlin/src/HAL/STM32F1/onboard_sd.cpp b/Marlin/src/HAL/STM32F1/onboard_sd.cpp
new file mode 100644
index 0000000000..a3d8dcb2d5
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/onboard_sd.cpp
@@ -0,0 +1,571 @@
+/**
+ * STM32F1: MMCv3/SDv1/SDv2 (SPI mode) control module
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2019 BigTreeTech [https://github.com/bigtreetech]
+ * Copyright (C) 2015, ChaN, all right reserved.
+ *
+ * This software is a free software and there is NO WARRANTY.
+ * No restriction on use. You can use, modify and redistribute it for
+ * personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
+ * Redistributions of source code must retain the above copyright notice.
+ */
+
+#ifdef __STM32F1__
+
+#include "../../inc/MarlinConfig.h"
+
+#if SD_CONNECTION_IS(ONBOARD)
+
+#include "onboard_sd.h"
+#include "SPI.h"
+#include "fastio.h"
+
+#ifndef ONBOARD_SPI_DEVICE
+ #define ONBOARD_SPI_DEVICE SPI_DEVICE
+#endif
+
+#if HAS_SD_HOST_DRIVE
+ #define ONBOARD_SD_SPI SPI
+#else
+ SPIClass OnboardSPI(ONBOARD_SPI_DEVICE);
+ #define ONBOARD_SD_SPI OnboardSPI
+#endif
+
+#if ONBOARD_SPI_DEVICE == 1
+ #define SPI_CLOCK_MAX SPI_BAUD_PCLK_DIV_4
+#else
+ #define SPI_CLOCK_MAX SPI_BAUD_PCLK_DIV_2
+#endif
+
+#if PIN_EXISTS(ONBOARD_SD_CS) && ONBOARD_SD_CS_PIN != SD_SS_PIN
+ #define CS_LOW() WRITE(ONBOARD_SD_CS_PIN, LOW) // Set OnboardSPI cs low
+ #define CS_HIGH() WRITE(ONBOARD_SD_CS_PIN, HIGH) // Set OnboardSPI cs high
+#else
+ #define CS_LOW()
+ #define CS_HIGH()
+#endif
+
+#define FCLK_FAST() ONBOARD_SD_SPI.setClockDivider(SPI_CLOCK_MAX)
+#define FCLK_SLOW() ONBOARD_SD_SPI.setClockDivider(SPI_BAUD_PCLK_DIV_256)
+
+/*--------------------------------------------------------------------------
+ Module Private Functions
+---------------------------------------------------------------------------*/
+
+/* MMC/SD command */
+#define CMD0 (0) // GO_IDLE_STATE
+#define CMD1 (1) // SEND_OP_COND (MMC)
+#define ACMD41 (0x80+41) // SEND_OP_COND (SDC)
+#define CMD8 (8) // SEND_IF_COND
+#define CMD9 (9) // SEND_CSD
+#define CMD10 (10) // SEND_CID
+#define CMD12 (12) // STOP_TRANSMISSION
+#define ACMD13 (0x80+13) // SD_STATUS (SDC)
+#define CMD16 (16) // SET_BLOCKLEN
+#define CMD17 (17) // READ_SINGLE_BLOCK
+#define CMD18 (18) // READ_MULTIPLE_BLOCK
+#define CMD23 (23) // SET_BLOCK_COUNT (MMC)
+#define ACMD23 (0x80+23) // SET_WR_BLK_ERASE_COUNT (SDC)
+#define CMD24 (24) // WRITE_BLOCK
+#define CMD25 (25) // WRITE_MULTIPLE_BLOCK
+#define CMD32 (32) // ERASE_ER_BLK_START
+#define CMD33 (33) // ERASE_ER_BLK_END
+#define CMD38 (38) // ERASE
+#define CMD48 (48) // READ_EXTR_SINGLE
+#define CMD49 (49) // WRITE_EXTR_SINGLE
+#define CMD55 (55) // APP_CMD
+#define CMD58 (58) // READ_OCR
+
+static volatile DSTATUS Stat = STA_NOINIT; // Physical drive status
+static volatile UINT timeout;
+static BYTE CardType; // Card type flags
+
+/*-----------------------------------------------------------------------*/
+/* Send/Receive data to the MMC (Platform dependent) */
+/*-----------------------------------------------------------------------*/
+
+/* Exchange a byte */
+static BYTE xchg_spi (
+ BYTE dat // Data to send
+) {
+ BYTE returnByte = ONBOARD_SD_SPI.transfer(dat);
+ return returnByte;
+}
+
+/* Receive multiple byte */
+static void rcvr_spi_multi (
+ BYTE *buff, // Pointer to data buffer
+ UINT btr // Number of bytes to receive (16, 64 or 512)
+) {
+ ONBOARD_SD_SPI.dmaTransfer(0, const_cast(buff), btr);
+}
+
+#if _DISKIO_WRITE
+
+ // Send multiple bytes
+ static void xmit_spi_multi (
+ const BYTE *buff, // Pointer to the data
+ UINT btx // Number of bytes to send (multiple of 16)
+ ) {
+ ONBOARD_SD_SPI.dmaSend(const_cast(buff), btx);
+ }
+
+#endif // _DISKIO_WRITE
+
+/*-----------------------------------------------------------------------*/
+/* Wait for card ready */
+/*-----------------------------------------------------------------------*/
+
+static int wait_ready ( // 1:Ready, 0:Timeout
+ UINT wt // Timeout [ms]
+) {
+ BYTE d;
+ timeout = millis() + wt;
+ do {
+ d = xchg_spi(0xFF);
+ // This loop takes a while. Insert rot_rdq() here for multitask environment.
+ } while (d != 0xFF && (timeout > millis())); // Wait for card goes ready or timeout
+
+ return (d == 0xFF) ? 1 : 0;
+}
+
+/*-----------------------------------------------------------------------*/
+/* Deselect card and release SPI */
+/*-----------------------------------------------------------------------*/
+
+static void deselect() {
+ CS_HIGH(); // CS = H
+ xchg_spi(0xFF); // Dummy clock (force DO hi-z for multiple slave SPI)
+}
+
+/*-----------------------------------------------------------------------*/
+/* Select card and wait for ready */
+/*-----------------------------------------------------------------------*/
+
+static int select() { // 1:OK, 0:Timeout
+ CS_LOW(); // CS = L
+ xchg_spi(0xFF); // Dummy clock (force DO enabled)
+
+ if (wait_ready(500)) return 1; // Leading busy check: Wait for card ready
+
+ deselect(); // Timeout
+ return 0;
+}
+
+/*-----------------------------------------------------------------------*/
+/* Control SPI module (Platform dependent) */
+/*-----------------------------------------------------------------------*/
+
+// Enable SSP module and attach it to I/O pads
+static void sd_power_on() {
+ ONBOARD_SD_SPI.setModule(ONBOARD_SPI_DEVICE);
+ ONBOARD_SD_SPI.begin();
+ ONBOARD_SD_SPI.setBitOrder(MSBFIRST);
+ ONBOARD_SD_SPI.setDataMode(SPI_MODE0);
+ CS_HIGH();
+}
+
+// Disable SPI function
+static void sd_power_off() {
+ select(); // Wait for card ready
+ deselect();
+}
+
+/*-----------------------------------------------------------------------*/
+/* Receive a data packet from the MMC */
+/*-----------------------------------------------------------------------*/
+
+static int rcvr_datablock ( // 1:OK, 0:Error
+ BYTE *buff, // Data buffer
+ UINT btr // Data block length (byte)
+) {
+ BYTE token;
+
+ timeout = millis() + 200;
+ do { // Wait for DataStart token in timeout of 200ms
+ token = xchg_spi(0xFF);
+ // This loop will take a while. Insert rot_rdq() here for multitask environment.
+ } while ((token == 0xFF) && (timeout > millis()));
+ if (token != 0xFE) return 0; // Function fails if invalid DataStart token or timeout
+
+ rcvr_spi_multi(buff, btr); // Store trailing data to the buffer
+ xchg_spi(0xFF); xchg_spi(0xFF); // Discard CRC
+
+ return 1; // Function succeeded
+}
+
+/*-----------------------------------------------------------------------*/
+/* Send a data packet to the MMC */
+/*-----------------------------------------------------------------------*/
+
+#if _DISKIO_WRITE
+
+ static int xmit_datablock( // 1:OK, 0:Failed
+ const BYTE *buff, // Pointer to 512 byte data to be sent
+ BYTE token // Token
+ ) {
+ BYTE resp;
+
+ if (!wait_ready(500)) return 0; // Leading busy check: Wait for card ready to accept data block
+
+ xchg_spi(token); // Send token
+ if (token == 0xFD) return 1; // Do not send data if token is StopTran
+
+ xmit_spi_multi(buff, 512); // Data
+ xchg_spi(0xFF); xchg_spi(0xFF); // Dummy CRC
+
+ resp = xchg_spi(0xFF); // Receive data resp
+
+ return (resp & 0x1F) == 0x05 ? 1 : 0; // Data was accepted or not
+
+ // Busy check is done at next transmission
+ }
+
+#endif // _DISKIO_WRITE
+
+/*-----------------------------------------------------------------------*/
+/* Send a command packet to the MMC */
+/*-----------------------------------------------------------------------*/
+
+static BYTE send_cmd( // Return value: R1 resp (bit7==1:Failed to send)
+ BYTE cmd, // Command index
+ DWORD arg // Argument
+) {
+ BYTE n, res;
+
+ if (cmd & 0x80) { // Send a CMD55 prior to ACMD
+ cmd &= 0x7F;
+ res = send_cmd(CMD55, 0);
+ if (res > 1) return res;
+ }
+
+ // Select the card and wait for ready except to stop multiple block read
+ if (cmd != CMD12) {
+ deselect();
+ if (!select()) return 0xFF;
+ }
+
+ // Send command packet
+ xchg_spi(0x40 | cmd); // Start + command index
+ xchg_spi((BYTE)(arg >> 24)); // Argument[31..24]
+ xchg_spi((BYTE)(arg >> 16)); // Argument[23..16]
+ xchg_spi((BYTE)(arg >> 8)); // Argument[15..8]
+ xchg_spi((BYTE)arg); // Argument[7..0]
+ n = 0x01; // Dummy CRC + Stop
+ if (cmd == CMD0) n = 0x95; // Valid CRC for CMD0(0)
+ if (cmd == CMD8) n = 0x87; // Valid CRC for CMD8(0x1AA)
+ xchg_spi(n);
+
+ // Receive command response
+ if (cmd == CMD12) xchg_spi(0xFF); // Discard the following byte when CMD12
+ n = 10; // Wait for response (10 bytes max)
+ do
+ res = xchg_spi(0xFF);
+ while ((res & 0x80) && --n);
+
+ return res; // Return received response
+}
+
+/*--------------------------------------------------------------------------
+ Public Functions
+---------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------*/
+/* Initialize disk drive */
+/*-----------------------------------------------------------------------*/
+
+DSTATUS disk_initialize (
+ BYTE drv // Physical drive number (0)
+) {
+ BYTE n, cmd, ty, ocr[4];
+
+ if (drv) return STA_NOINIT; // Supports only drive 0
+ sd_power_on(); // Initialize SPI
+
+ if (Stat & STA_NODISK) return Stat; // Is a card existing in the socket?
+
+ FCLK_SLOW();
+ for (n = 10; n; n--) xchg_spi(0xFF); // Send 80 dummy clocks
+
+ ty = 0;
+ if (send_cmd(CMD0, 0) == 1) { // Put the card SPI state
+ timeout = millis() + 1000; // Initialization timeout = 1 sec
+ if (send_cmd(CMD8, 0x1AA) == 1) { // Is the catd SDv2?
+ for (n = 0; n < 4; n++) ocr[n] = xchg_spi(0xFF); // Get 32 bit return value of R7 resp
+ if (ocr[2] == 0x01 && ocr[3] == 0xAA) { // Does the card support 2.7-3.6V?
+ while ((timeout > millis()) && send_cmd(ACMD41, 1UL << 30)); // Wait for end of initialization with ACMD41(HCS)
+ if ((timeout > millis()) && send_cmd(CMD58, 0) == 0) { // Check CCS bit in the OCR
+ for (n = 0; n < 4; n++) ocr[n] = xchg_spi(0xFF);
+ ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; // Check if the card is SDv2
+ }
+ }
+ }
+ else { // Not an SDv2 card
+ if (send_cmd(ACMD41, 0) <= 1) { // SDv1 or MMCv3?
+ ty = CT_SD1; cmd = ACMD41; // SDv1 (ACMD41(0))
+ }
+ else {
+ ty = CT_MMC; cmd = CMD1; // MMCv3 (CMD1(0))
+ }
+ while ((timeout > millis()) && send_cmd(cmd, 0)); // Wait for the card leaves idle state
+ if (!(timeout > millis()) || send_cmd(CMD16, 512) != 0) // Set block length: 512
+ ty = 0;
+ }
+ }
+ CardType = ty; // Card type
+ deselect();
+
+ if (ty) { // OK
+ FCLK_FAST(); // Set fast clock
+ Stat &= ~STA_NOINIT; // Clear STA_NOINIT flag
+ }
+ else { // Failed
+ sd_power_off();
+ Stat = STA_NOINIT;
+ }
+
+ return Stat;
+}
+
+/*-----------------------------------------------------------------------*/
+/* Get disk status */
+/*-----------------------------------------------------------------------*/
+
+DSTATUS disk_status (
+ BYTE drv // Physical drive number (0)
+) {
+ if (drv) return STA_NOINIT; // Supports only drive 0
+ return Stat; // Return disk status
+}
+
+/*-----------------------------------------------------------------------*/
+/* Read sector(s) */
+/*-----------------------------------------------------------------------*/
+
+DRESULT disk_read (
+ BYTE drv, // Physical drive number (0)
+ BYTE *buff, // Pointer to the data buffer to store read data
+ DWORD sector, // Start sector number (LBA)
+ UINT count // Number of sectors to read (1..128)
+) {
+ BYTE cmd;
+
+ if (drv || !count) return RES_PARERR; // Check parameter
+ if (Stat & STA_NOINIT) return RES_NOTRDY; // Check if drive is ready
+ if (!(CardType & CT_BLOCK)) sector *= 512; // LBA ot BA conversion (byte addressing cards)
+ FCLK_FAST();
+ cmd = count > 1 ? CMD18 : CMD17; // READ_MULTIPLE_BLOCK : READ_SINGLE_BLOCK
+ if (send_cmd(cmd, sector) == 0) {
+ do {
+ if (!rcvr_datablock(buff, 512)) break;
+ buff += 512;
+ } while (--count);
+ if (cmd == CMD18) send_cmd(CMD12, 0); // STOP_TRANSMISSION
+ }
+ deselect();
+
+ return count ? RES_ERROR : RES_OK; // Return result
+}
+
+/*-----------------------------------------------------------------------*/
+/* Write sector(s) */
+/*-----------------------------------------------------------------------*/
+
+#if _DISKIO_WRITE
+
+ DRESULT disk_write(
+ BYTE drv, // Physical drive number (0)
+ const BYTE *buff, // Pointer to the data to write
+ DWORD sector, // Start sector number (LBA)
+ UINT count // Number of sectors to write (1..128)
+ ) {
+ if (drv || !count) return RES_PARERR; // Check parameter
+ if (Stat & STA_NOINIT) return RES_NOTRDY; // Check drive status
+ if (Stat & STA_PROTECT) return RES_WRPRT; // Check write protect
+ FCLK_FAST();
+ if (!(CardType & CT_BLOCK)) sector *= 512; // LBA ==> BA conversion (byte addressing cards)
+
+ if (count == 1) { // Single sector write
+ if ((send_cmd(CMD24, sector) == 0) // WRITE_BLOCK
+ && xmit_datablock(buff, 0xFE)) {
+ count = 0;
+ }
+ }
+ else { // Multiple sector write
+ if (CardType & CT_SDC) send_cmd(ACMD23, count); // Predefine number of sectors
+ if (send_cmd(CMD25, sector) == 0) { // WRITE_MULTIPLE_BLOCK
+ do {
+ if (!xmit_datablock(buff, 0xFC)) break;
+ buff += 512;
+ } while (--count);
+ if (!xmit_datablock(0, 0xFD)) count = 1; // STOP_TRAN token
+ }
+ }
+ deselect();
+
+ return count ? RES_ERROR : RES_OK; // Return result
+ }
+
+#endif // _DISKIO_WRITE
+
+/*-----------------------------------------------------------------------*/
+/* Miscellaneous drive controls other than data read/write */
+/*-----------------------------------------------------------------------*/
+
+#if _DISKIO_IOCTL
+
+ DRESULT disk_ioctl (
+ BYTE drv, // Physical drive number (0)
+ BYTE cmd, // Control command code
+ void *buff // Pointer to the conrtol data
+ ) {
+ DRESULT res;
+ BYTE n, csd[16], *ptr = (BYTE *)buff;
+ DWORD *dp, st, ed, csize;
+ #if _DISKIO_ISDIO
+ SDIO_CMD *sdio = buff;
+ BYTE rc, *buf;
+ UINT dc;
+ #endif
+
+ if (drv) return RES_PARERR; // Check parameter
+ if (Stat & STA_NOINIT) return RES_NOTRDY; // Check if drive is ready
+
+ res = RES_ERROR;
+ FCLK_FAST();
+ switch (cmd) {
+ case CTRL_SYNC: // Wait for end of internal write process of the drive
+ if (select()) res = RES_OK;
+ break;
+
+ case GET_SECTOR_COUNT: // Get drive capacity in unit of sector (DWORD)
+ if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
+ if ((csd[0] >> 6) == 1) { // SDC ver 2.00
+ csize = csd[9] + ((WORD)csd[8] << 8) + ((DWORD)(csd[7] & 63) << 16) + 1;
+ *(DWORD*)buff = csize << 10;
+ }
+ else { // SDC ver 1.XX or MMC ver 3
+ n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
+ csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
+ *(DWORD*)buff = csize << (n - 9);
+ }
+ res = RES_OK;
+ }
+ break;
+
+ case GET_BLOCK_SIZE: // Get erase block size in unit of sector (DWORD)
+ if (CardType & CT_SD2) { // SDC ver 2.00
+ if (send_cmd(ACMD13, 0) == 0) { // Read SD status
+ xchg_spi(0xFF);
+ if (rcvr_datablock(csd, 16)) { // Read partial block
+ for (n = 64 - 16; n; n--) xchg_spi(0xFF); // Purge trailing data
+ *(DWORD*)buff = 16UL << (csd[10] >> 4);
+ res = RES_OK;
+ }
+ }
+ }
+ else { // SDC ver 1.XX or MMC
+ if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { // Read CSD
+ if (CardType & CT_SD1) { // SDC ver 1.XX
+ *(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
+ }
+ else { // MMC
+ *(DWORD*)buff = ((WORD)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
+ }
+ res = RES_OK;
+ }
+ }
+ break;
+
+ case CTRL_TRIM: // Erase a block of sectors (used when _USE_TRIM in ffconf.h is 1)
+ if (!(CardType & CT_SDC)) break; // Check if the card is SDC
+ if (disk_ioctl(drv, MMC_GET_CSD, csd)) break; // Get CSD
+ if (!(csd[0] >> 6) && !(csd[10] & 0x40)) break; // Check if sector erase can be applied to the card
+ dp = (DWORD *)buff; st = dp[0]; ed = dp[1]; // Load sector block
+ if (!(CardType & CT_BLOCK)) {
+ st *= 512; ed *= 512;
+ }
+ if (send_cmd(CMD32, st) == 0 && send_cmd(CMD33, ed) == 0 && send_cmd(CMD38, 0) == 0 && wait_ready(30000)) { // Erase sector block
+ res = RES_OK; // FatFs does not check result of this command
+ }
+ break;
+
+ // The following commands are never used by FatFs module
+
+ case MMC_GET_TYPE: // Get MMC/SDC type (BYTE)
+ *ptr = CardType;
+ res = RES_OK;
+ break;
+
+ case MMC_GET_CSD: // Read CSD (16 bytes)
+ if (send_cmd(CMD9, 0) == 0 && rcvr_datablock(ptr, 16)) {
+ res = RES_OK;
+ }
+ break;
+
+ case MMC_GET_CID: // Read CID (16 bytes)
+ if (send_cmd(CMD10, 0) == 0 && rcvr_datablock(ptr, 16)) {
+ res = RES_OK;
+ }
+ break;
+
+ case MMC_GET_OCR: // Read OCR (4 bytes)
+ if (send_cmd(CMD58, 0) == 0) {
+ for (n = 4; n; n--) *ptr++ = xchg_spi(0xFF);
+ res = RES_OK;
+ }
+ break;
+
+ case MMC_GET_SDSTAT: // Read SD status (64 bytes)
+ if (send_cmd(ACMD13, 0) == 0) {
+ xchg_spi(0xFF);
+ if (rcvr_datablock(ptr, 64)) res = RES_OK;
+ }
+ break;
+
+ #if _DISKIO_ISDIO
+
+ case ISDIO_READ:
+ sdio = buff;
+ if (send_cmd(CMD48, 0x80000000 | sdio->func << 28 | sdio->addr << 9 | ((sdio->ndata - 1) & 0x1FF)) == 0) {
+ for (Timer1 = 1000; (rc = xchg_spi(0xFF)) == 0xFF && Timer1; ) ;
+ if (rc == 0xFE) {
+ for (buf = sdio->data, dc = sdio->ndata; dc; dc--) *buf++ = xchg_spi(0xFF);
+ for (dc = 514 - sdio->ndata; dc; dc--) xchg_spi(0xFF);
+ res = RES_OK;
+ }
+ }
+ break;
+ case ISDIO_WRITE:
+ sdio = buff;
+ if (send_cmd(CMD49, 0x80000000 | sdio->func << 28 | sdio->addr << 9 | ((sdio->ndata - 1) & 0x1FF)) == 0) {
+ xchg_spi(0xFF); xchg_spi(0xFE);
+ for (buf = sdio->data, dc = sdio->ndata; dc; dc--) xchg_spi(*buf++);
+ for (dc = 514 - sdio->ndata; dc; dc--) xchg_spi(0xFF);
+ if ((xchg_spi(0xFF) & 0x1F) == 0x05) res = RES_OK;
+ }
+ break;
+ case ISDIO_MRITE:
+ sdio = buff;
+ if (send_cmd(CMD49, 0x84000000 | sdio->func << 28 | sdio->addr << 9 | sdio->ndata >> 8) == 0) {
+ xchg_spi(0xFF); xchg_spi(0xFE);
+ xchg_spi(sdio->ndata);
+ for (dc = 513; dc; dc--) xchg_spi(0xFF);
+ if ((xchg_spi(0xFF) & 0x1F) == 0x05) res = RES_OK;
+ }
+ break;
+
+ #endif // _DISKIO_ISDIO
+
+ default: res = RES_PARERR;
+ }
+
+ deselect();
+ return res;
+ }
+
+#endif // _DISKIO_IOCTL
+
+#endif // SD_CONNECTION_IS(ONBOARD)
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/onboard_sd.h b/Marlin/src/HAL/STM32F1/onboard_sd.h
new file mode 100644
index 0000000000..f8846e95bc
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/onboard_sd.h
@@ -0,0 +1,116 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+/*-----------------------------------------------------------------------
+/ * Copyright (c) 2019 BigTreeTech [https://github.com/bigtreetech]
+/ * Low level disk interface module include file (c) ChaN, 2015
+/-----------------------------------------------------------------------*/
+
+#define _DISKIO_WRITE 1 /* 1: Enable disk_write function */
+#define _DISKIO_IOCTL 1 /* 1: Enable disk_ioctl function */
+#define _DISKIO_ISDIO 0 /* 1: Enable iSDIO control function */
+
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+typedef unsigned int UINT;
+
+/* Status of Disk Functions */
+typedef BYTE DSTATUS;
+
+/* Results of Disk Functions */
+typedef enum {
+ RES_OK = 0, /* 0: Successful */
+ RES_ERROR, /* 1: R/W Error */
+ RES_WRPRT, /* 2: Write Protected */
+ RES_NOTRDY, /* 3: Not Ready */
+ RES_PARERR /* 4: Invalid Parameter */
+} DRESULT;
+
+
+#if _DISKIO_ISDIO
+/* Command structure for iSDIO ioctl command */
+typedef struct {
+ BYTE func; /* Function number: 0..7 */
+ WORD ndata; /* Number of bytes to transfer: 1..512, or mask + data */
+ DWORD addr; /* Register address: 0..0x1FFFF */
+ void* data; /* Pointer to the data (to be written | read buffer) */
+} SDIO_CMD;
+#endif
+
+/*---------------------------------------*/
+/* Prototypes for disk control functions */
+
+DSTATUS disk_initialize(BYTE pdrv);
+DSTATUS disk_status(BYTE pdrv);
+DRESULT disk_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
+#if _DISKIO_WRITE
+ DRESULT disk_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
+#endif
+#if _DISKIO_IOCTL
+ DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff);
+#endif
+
+/* Disk Status Bits (DSTATUS) */
+#define STA_NOINIT 0x01 /* Drive not initialized */
+#define STA_NODISK 0x02 /* No medium in the drive */
+#define STA_PROTECT 0x04 /* Write protected */
+
+/* Command code for disk_ioctrl function */
+
+/* Generic command (Used by FatFs) */
+#define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */
+#define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */
+#define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */
+#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */
+#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */
+
+/* Generic command (Not used by FatFs) */
+#define CTRL_FORMAT 5 /* Create physical format on the media */
+#define CTRL_POWER_IDLE 6 /* Put the device idle state */
+#define CTRL_POWER_OFF 7 /* Put the device off state */
+#define CTRL_LOCK 8 /* Lock media removal */
+#define CTRL_UNLOCK 9 /* Unlock media removal */
+#define CTRL_EJECT 10 /* Eject media */
+
+/* MMC/SDC specific ioctl command (Not used by FatFs) */
+#define MMC_GET_TYPE 50 /* Get card type */
+#define MMC_GET_CSD 51 /* Get CSD */
+#define MMC_GET_CID 52 /* Get CID */
+#define MMC_GET_OCR 53 /* Get OCR */
+#define MMC_GET_SDSTAT 54 /* Get SD status */
+#define ISDIO_READ 55 /* Read data form SD iSDIO register */
+#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */
+#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */
+
+/* ATA/CF specific ioctl command (Not used by FatFs) */
+#define ATA_GET_REV 60 /* Get F/W revision */
+#define ATA_GET_MODEL 61 /* Get model name */
+#define ATA_GET_SN 62 /* Get serial number */
+
+/* MMC card type flags (MMC_GET_TYPE) */
+#define CT_MMC 0x01 /* MMC ver 3 */
+#define CT_SD1 0x02 /* SD ver 1 */
+#define CT_SD2 0x04 /* SD ver 2 */
+#define CT_SDC (CT_SD1|CT_SD2) /* SD */
+#define CT_BLOCK 0x08 /* Block addressing */
diff --git a/Marlin/src/HAL/STM32F1/pinsDebug.h b/Marlin/src/HAL/STM32F1/pinsDebug.h
new file mode 100644
index 0000000000..7828479658
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/pinsDebug.h
@@ -0,0 +1,123 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+/**
+ * Support routines for MAPLE_STM32F1
+ */
+
+/**
+ * Translation of routines & variables used by pinsDebug.h
+ */
+
+#ifndef BOARD_NR_GPIO_PINS // Only in MAPLE_STM32F1
+ #error "Expected BOARD_NR_GPIO_PINS not found"
+#endif
+
+#include "fastio.h"
+
+extern const stm32_pin_info PIN_MAP[BOARD_NR_GPIO_PINS];
+
+#define NUM_DIGITAL_PINS BOARD_NR_GPIO_PINS
+#define NUMBER_PINS_TOTAL BOARD_NR_GPIO_PINS
+#define VALID_PIN(pin) (pin >= 0 && pin < BOARD_NR_GPIO_PINS)
+#define GET_ARRAY_PIN(p) pin_t(pin_array[p].pin)
+#define pwm_status(pin) PWM_PIN(pin)
+#define digitalRead_mod(p) extDigitalRead(p)
+#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%3hd "), int16_t(p)); SERIAL_ECHO(buffer); }while(0)
+#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0)
+#define PRINT_PORT(p) print_port(p)
+#define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0)
+#define MULTI_NAME_PAD 21 // space needed to be pretty if not first name assigned to a pin
+
+// pins that will cause hang/reset/disconnect in M43 Toggle and Watch utilities
+#ifndef M43_NEVER_TOUCH
+ #define M43_NEVER_TOUCH(Q) (Q >= 9 && Q <= 12) // SERIAL/USB pins PA9(TX) PA10(RX)
+#endif
+
+static int8_t get_pin_mode(pin_t pin) {
+ return VALID_PIN(pin) ? _GET_MODE(pin) : -1;
+}
+
+static pin_t DIGITAL_PIN_TO_ANALOG_PIN(pin_t pin) {
+ if (!VALID_PIN(pin)) return -1;
+ int8_t adc_channel = int8_t(PIN_MAP[pin].adc_channel);
+ #ifdef NUM_ANALOG_INPUTS
+ if (adc_channel >= NUM_ANALOG_INPUTS) adc_channel = ADCx;
+ #endif
+ return pin_t(adc_channel);
+}
+
+static bool IS_ANALOG(pin_t pin) {
+ if (!VALID_PIN(pin)) return false;
+ if (PIN_MAP[pin].adc_channel != ADCx) {
+ #ifdef NUM_ANALOG_INPUTS
+ if (PIN_MAP[pin].adc_channel >= NUM_ANALOG_INPUTS) return false;
+ #endif
+ return _GET_MODE(pin) == GPIO_INPUT_ANALOG && !M43_NEVER_TOUCH(pin);
+ }
+ return false;
+}
+
+static bool GET_PINMODE(const pin_t pin) {
+ return VALID_PIN(pin) && !IS_INPUT(pin);
+}
+
+static bool GET_ARRAY_IS_DIGITAL(const int16_t array_pin) {
+ const pin_t pin = GET_ARRAY_PIN(array_pin);
+ return (!IS_ANALOG(pin)
+ #ifdef NUM_ANALOG_INPUTS
+ || PIN_MAP[pin].adc_channel >= NUM_ANALOG_INPUTS
+ #endif
+ );
+}
+
+#include "../../inc/MarlinConfig.h" // Allow pins/pins.h to set density
+
+static void pwm_details(const pin_t pin) {
+ if (PWM_PIN(pin)) {
+ timer_dev * const tdev = PIN_MAP[pin].timer_device;
+ const uint8_t channel = PIN_MAP[pin].timer_channel;
+ const char num = (
+ #if EITHER(STM32_HIGH_DENSITY, STM32_XL_DENSITY)
+ tdev == &timer8 ? '8' :
+ tdev == &timer5 ? '5' :
+ #endif
+ tdev == &timer4 ? '4' :
+ tdev == &timer3 ? '3' :
+ tdev == &timer2 ? '2' :
+ tdev == &timer1 ? '1' : '?'
+ );
+ char buffer[10];
+ sprintf_P(buffer, PSTR(" TIM%c CH%c"), num, ('0' + channel));
+ SERIAL_ECHO(buffer);
+ }
+}
+
+static void print_port(pin_t pin) {
+ const char port = 'A' + char(pin >> 4); // pin div 16
+ const int16_t gbit = PIN_MAP[pin].gpio_bit;
+ char buffer[8];
+ sprintf_P(buffer, PSTR("P%c%hd "), port, gbit);
+ if (gbit < 10) SERIAL_CHAR(' ');
+ SERIAL_ECHO(buffer);
+}
diff --git a/Marlin/src/HAL/STM32F1/sdio.cpp b/Marlin/src/HAL/STM32F1/sdio.cpp
new file mode 100644
index 0000000000..6e41d2cbf1
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/sdio.cpp
@@ -0,0 +1,307 @@
+/**
+ * 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
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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 .
+ *
+ */
+#ifdef ARDUINO_ARCH_STM32F1
+
+#include
+
+#include "../../inc/MarlinConfig.h" // Allow pins/pins.h to set density
+
+#if EITHER(STM32_HIGH_DENSITY, STM32_XL_DENSITY)
+
+#include "sdio.h"
+
+SDIO_CardInfoTypeDef SdCard;
+
+bool SDIO_Init() {
+ uint32_t count = 0U;
+ SdCard.CardType = SdCard.CardVersion = SdCard.Class = SdCard.RelCardAdd = SdCard.BlockNbr = SdCard.BlockSize = SdCard.LogBlockNbr = SdCard.LogBlockSize = 0;
+
+ sdio_begin();
+ sdio_set_dbus_width(SDIO_CLKCR_WIDBUS_1BIT);
+
+ dma_init(SDIO_DMA_DEV);
+ dma_disable(SDIO_DMA_DEV, SDIO_DMA_CHANNEL);
+ dma_set_priority(SDIO_DMA_DEV, SDIO_DMA_CHANNEL, DMA_PRIORITY_MEDIUM);
+
+ if (!SDIO_CmdGoIdleState()) return false;
+ if (!SDIO_CmdGoIdleState()) return false; /* Hotplugged cards tends to miss first CMD0, so give them a second chance. */
+
+ SdCard.CardVersion = SDIO_CmdOperCond() ? CARD_V2_X : CARD_V1_X;
+
+ do {
+ if (count++ == SDMMC_MAX_VOLT_TRIAL) return false;
+ SDIO_CmdAppOperCommand(SdCard.CardVersion == CARD_V2_X ? SDMMC_HIGH_CAPACITY : SDMMC_STD_CAPACITY);
+ } while ((SDIO_GetResponse(SDIO_RESP1) & 0x80000000) == 0);
+
+ SdCard.CardType = (SDIO_GetResponse(SDIO_RESP1) & SDMMC_HIGH_CAPACITY) ? CARD_SDHC_SDXC : CARD_SDSC;
+
+ if (!SDIO_CmdSendCID()) return false;
+ if (!SDIO_CmdSetRelAdd(&SdCard.RelCardAdd)) return false; /* Send CMD3 SET_REL_ADDR with argument 0. SD Card publishes its RCA. */
+ if (!SDIO_CmdSendCSD(SdCard.RelCardAdd << 16U)) return false;
+
+ SdCard.Class = (SDIO_GetResponse(SDIO_RESP2) >> 20U);
+
+ if (SdCard.CardType == CARD_SDHC_SDXC) {
+ SdCard.LogBlockNbr = SdCard.BlockNbr = (((SDIO_GetResponse(SDIO_RESP2) & 0x0000003FU) << 26U) | ((SDIO_GetResponse(SDIO_RESP3) & 0xFFFF0000U) >> 6U)) + 1024;
+ SdCard.LogBlockSize = SdCard.BlockSize = 512U;
+ }
+ else {
+ SdCard.BlockNbr = ((((SDIO_GetResponse(SDIO_RESP2) & 0x000003FFU) << 2U ) | ((SDIO_GetResponse(SDIO_RESP3) & 0xC0000000U) >> 30U)) + 1U) * (4U << ((SDIO_GetResponse(SDIO_RESP3) & 0x00038000U) >> 15U));
+ SdCard.BlockSize = 1U << ((SDIO_GetResponse(SDIO_RESP2) >> 16) & 0x0FU);
+ SdCard.LogBlockNbr = (SdCard.BlockNbr) * ((SdCard.BlockSize) / 512U);
+ SdCard.LogBlockSize = 512U;
+ }
+
+ if (!SDIO_CmdSelDesel(SdCard.RelCardAdd << 16U)) return false;
+ if (!SDIO_CmdAppSetClearCardDetect(SdCard.RelCardAdd << 16U)) return false;
+ if (!SDIO_CmdAppSetBusWidth(SdCard.RelCardAdd << 16U, 2)) return false;
+
+ sdio_set_dbus_width(SDIO_CLKCR_WIDBUS_4BIT);
+ sdio_set_clock(SDIO_CLOCK);
+ return true;
+}
+
+bool SDIO_ReadBlock_DMA(uint32_t blockAddress, uint8_t *data) {
+ if (SDIO_GetCardState() != SDIO_CARD_TRANSFER) return false;
+ if (blockAddress >= SdCard.LogBlockNbr) return false;
+ if ((0x03 & (uint32_t)data)) return false; // misaligned data
+
+ if (SdCard.CardType != CARD_SDHC_SDXC) { blockAddress *= 512U; }
+
+ dma_setup_transfer(SDIO_DMA_DEV, SDIO_DMA_CHANNEL, &SDIO->FIFO, DMA_SIZE_32BITS, data, DMA_SIZE_32BITS, DMA_MINC_MODE);
+ dma_set_num_transfers(SDIO_DMA_DEV, SDIO_DMA_CHANNEL, 128);
+ dma_clear_isr_bits(SDIO_DMA_DEV, SDIO_DMA_CHANNEL);
+ dma_enable(SDIO_DMA_DEV, SDIO_DMA_CHANNEL);
+
+ sdio_setup_transfer(SDIO_DATA_TIMEOUT * (F_CPU / 1000U), 512, SDIO_BLOCKSIZE_512 | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTEN | SDIO_DIR_RX);
+
+ if (!SDIO_CmdReadSingleBlock(blockAddress)) {
+ SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS);
+ dma_disable(SDIO_DMA_DEV, SDIO_DMA_CHANNEL);
+ return false;
+ }
+
+ while (!SDIO_GET_FLAG(SDIO_STA_DATAEND | SDIO_STA_TRX_ERROR_FLAGS)) { /* wait */ }
+
+ //If there were SDIO errors, do not wait DMA.
+ if (SDIO->STA & SDIO_STA_TRX_ERROR_FLAGS) {
+ SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS | SDIO_ICR_DATA_FLAGS);
+ dma_disable(SDIO_DMA_DEV, SDIO_DMA_CHANNEL);
+ return false;
+ }
+
+ //Wait for DMA transaction to complete
+ while ((DMA2_BASE->ISR & (DMA_ISR_TEIF4|DMA_ISR_TCIF4)) == 0 ) { /* wait */ }
+
+ if (DMA2_BASE->ISR & DMA_ISR_TEIF4) {
+ dma_disable(SDIO_DMA_DEV, SDIO_DMA_CHANNEL);
+ SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS | SDIO_ICR_DATA_FLAGS);
+ return false;
+ }
+
+ dma_disable(SDIO_DMA_DEV, SDIO_DMA_CHANNEL);
+
+ if (SDIO->STA & SDIO_STA_RXDAVL) {
+ while (SDIO->STA & SDIO_STA_RXDAVL) (void)SDIO->FIFO;
+ SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS | SDIO_ICR_DATA_FLAGS);
+ return false;
+ }
+
+ if (SDIO_GET_FLAG(SDIO_STA_TRX_ERROR_FLAGS)) {
+ SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS | SDIO_ICR_DATA_FLAGS);
+ return false;
+ }
+ SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS | SDIO_ICR_DATA_FLAGS);
+ return true;
+}
+
+bool SDIO_ReadBlock(uint32_t blockAddress, uint8_t *data) {
+ uint32_t retries = SDIO_READ_RETRIES;
+ while (retries--) if (SDIO_ReadBlock_DMA(blockAddress, data)) return true;
+ return false;
+}
+
+uint32_t millis();
+
+bool SDIO_WriteBlock(uint32_t blockAddress, const uint8_t *data) {
+ if (SDIO_GetCardState() != SDIO_CARD_TRANSFER) return false;
+ if (blockAddress >= SdCard.LogBlockNbr) return false;
+ if ((0x03 & (uint32_t)data)) return false; // misaligned data
+
+ if (SdCard.CardType != CARD_SDHC_SDXC) { blockAddress *= 512U; }
+
+ dma_setup_transfer(SDIO_DMA_DEV, SDIO_DMA_CHANNEL, &SDIO->FIFO, DMA_SIZE_32BITS, (volatile void *) data, DMA_SIZE_32BITS, DMA_MINC_MODE | DMA_FROM_MEM);
+ dma_set_num_transfers(SDIO_DMA_DEV, SDIO_DMA_CHANNEL, 128);
+ dma_clear_isr_bits(SDIO_DMA_DEV, SDIO_DMA_CHANNEL);
+ dma_enable(SDIO_DMA_DEV, SDIO_DMA_CHANNEL);
+
+ if (!SDIO_CmdWriteSingleBlock(blockAddress)) {
+ dma_disable(SDIO_DMA_DEV, SDIO_DMA_CHANNEL);
+ return false;
+ }
+
+ sdio_setup_transfer(SDIO_DATA_TIMEOUT * (F_CPU / 1000U), 512U, SDIO_BLOCKSIZE_512 | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTEN);
+
+ while (!SDIO_GET_FLAG(SDIO_STA_DATAEND | SDIO_STA_TRX_ERROR_FLAGS)) { /* wait */ }
+
+ dma_disable(SDIO_DMA_DEV, SDIO_DMA_CHANNEL);
+
+ if (SDIO_GET_FLAG(SDIO_STA_TRX_ERROR_FLAGS)) {
+ SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS | SDIO_ICR_DATA_FLAGS);
+ return false;
+ }
+
+ SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS | SDIO_ICR_DATA_FLAGS);
+
+ uint32_t timeout = millis() + SDIO_WRITE_TIMEOUT;
+ while (timeout > millis()) {
+ if (SDIO_GetCardState() == SDIO_CARD_TRANSFER) {
+ return true;
+ }
+ }
+ return false;
+}
+
+inline uint32_t SDIO_GetCardState() { return SDIO_CmdSendStatus(SdCard.RelCardAdd << 16U) ? (SDIO_GetResponse(SDIO_RESP1) >> 9U) & 0x0FU : SDIO_CARD_ERROR; }
+
+// No F1 board with SDIO + MSC using Maple, that I aware of...
+bool SDIO_IsReady() { return true; }
+uint32_t SDIO_GetCardSize() { return 0; }
+
+// ------------------------
+// SD Commands and Responses
+// ------------------------
+
+void SDIO_SendCommand(uint16_t command, uint32_t argument) { SDIO->ARG = argument; SDIO->CMD = (uint32_t)(SDIO_CMD_CPSMEN | command); }
+uint8_t SDIO_GetCommandResponse() { return (uint8_t)(SDIO->RESPCMD); }
+uint32_t SDIO_GetResponse(uint32_t response) { return SDIO->RESP[response]; }
+
+bool SDIO_CmdGoIdleState() { SDIO_SendCommand(CMD0_GO_IDLE_STATE, 0); return SDIO_GetCmdError(); }
+bool SDIO_CmdSendCID() { SDIO_SendCommand(CMD2_ALL_SEND_CID, 0); return SDIO_GetCmdResp2(); }
+bool SDIO_CmdSetRelAdd(uint32_t *rca) { SDIO_SendCommand(CMD3_SET_REL_ADDR, 0); return SDIO_GetCmdResp6(SDMMC_CMD_SET_REL_ADDR, rca); }
+bool SDIO_CmdSelDesel(uint32_t address) { SDIO_SendCommand(CMD7_SEL_DESEL_CARD, address); return SDIO_GetCmdResp1(SDMMC_CMD_SEL_DESEL_CARD); }
+bool SDIO_CmdOperCond() { SDIO_SendCommand(CMD8_HS_SEND_EXT_CSD, SDMMC_CHECK_PATTERN); return SDIO_GetCmdResp7(); }
+bool SDIO_CmdSendCSD(uint32_t argument) { SDIO_SendCommand(CMD9_SEND_CSD, argument); return SDIO_GetCmdResp2(); }
+bool SDIO_CmdSendStatus(uint32_t argument) { SDIO_SendCommand(CMD13_SEND_STATUS, argument); return SDIO_GetCmdResp1(SDMMC_CMD_SEND_STATUS); }
+bool SDIO_CmdReadSingleBlock(uint32_t address) { SDIO_SendCommand(CMD17_READ_SINGLE_BLOCK, address); return SDIO_GetCmdResp1(SDMMC_CMD_READ_SINGLE_BLOCK); }
+bool SDIO_CmdWriteSingleBlock(uint32_t address) { SDIO_SendCommand(CMD24_WRITE_SINGLE_BLOCK, address); return SDIO_GetCmdResp1(SDMMC_CMD_WRITE_SINGLE_BLOCK); }
+bool SDIO_CmdAppCommand(uint32_t rsa) { SDIO_SendCommand(CMD55_APP_CMD, rsa); return SDIO_GetCmdResp1(SDMMC_CMD_APP_CMD); }
+
+bool SDIO_CmdAppSetBusWidth(uint32_t rsa, uint32_t argument) {
+ if (!SDIO_CmdAppCommand(rsa)) return false;
+ SDIO_SendCommand(ACMD6_APP_SD_SET_BUSWIDTH, argument);
+ return SDIO_GetCmdResp2();
+}
+
+bool SDIO_CmdAppOperCommand(uint32_t sdType) {
+ if (!SDIO_CmdAppCommand(0)) return false;
+ SDIO_SendCommand(ACMD41_SD_APP_OP_COND , SDMMC_VOLTAGE_WINDOW_SD | sdType);
+ return SDIO_GetCmdResp3();
+}
+
+bool SDIO_CmdAppSetClearCardDetect(uint32_t rsa) {
+ if (!SDIO_CmdAppCommand(rsa)) return false;
+ SDIO_SendCommand(ACMD42_SD_APP_SET_CLR_CARD_DETECT, 0);
+ return SDIO_GetCmdResp2();
+}
+
+// Wait until given flags are unset or till timeout
+#define SDIO_WAIT(FLAGS) do{ \
+ uint32_t count = 1 + (SDIO_CMDTIMEOUT) * ((F_CPU) / 8U / 1000U); \
+ do { if (!--count) return false; } while (!SDIO_GET_FLAG(FLAGS)); \
+}while(0)
+
+bool SDIO_GetCmdError() {
+ SDIO_WAIT(SDIO_STA_CMDSENT);
+
+ SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS);
+ return true;
+}
+
+bool SDIO_GetCmdResp1(uint8_t command) {
+ SDIO_WAIT(SDIO_STA_CCRCFAIL | SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT);
+
+ if (SDIO_GET_FLAG(SDIO_STA_CCRCFAIL | SDIO_STA_CTIMEOUT)) {
+ SDIO_CLEAR_FLAG(SDIO_STA_CCRCFAIL | SDIO_STA_CTIMEOUT);
+ return false;
+ }
+ if (SDIO_GetCommandResponse() != command) return false;
+
+ SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS);
+ return (SDIO_GetResponse(SDIO_RESP1) & SDMMC_OCR_ERRORBITS) == SDMMC_ALLZERO;
+}
+
+bool SDIO_GetCmdResp2() {
+ SDIO_WAIT(SDIO_STA_CCRCFAIL | SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT);
+
+ if (SDIO_GET_FLAG(SDIO_STA_CCRCFAIL | SDIO_STA_CTIMEOUT)) {
+ SDIO_CLEAR_FLAG(SDIO_STA_CCRCFAIL | SDIO_STA_CTIMEOUT);
+ return false;
+ }
+
+ SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS);
+ return true;
+}
+
+bool SDIO_GetCmdResp3() {
+ SDIO_WAIT(SDIO_STA_CCRCFAIL | SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT);
+
+ if (SDIO_GET_FLAG(SDIO_STA_CTIMEOUT)) {
+ SDIO_CLEAR_FLAG(SDIO_STA_CTIMEOUT);
+ return false;
+ }
+
+ SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS);
+ return true;
+}
+
+bool SDIO_GetCmdResp6(uint8_t command, uint32_t *rca) {
+ SDIO_WAIT(SDIO_STA_CCRCFAIL | SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT);
+
+ if (SDIO_GET_FLAG(SDIO_STA_CCRCFAIL | SDIO_STA_CTIMEOUT)) {
+ SDIO_CLEAR_FLAG(SDIO_STA_CCRCFAIL | SDIO_STA_CTIMEOUT);
+ return false;
+ }
+ if (SDIO_GetCommandResponse() != command) return false;
+
+ SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS);
+ if (SDIO_GetResponse(SDIO_RESP1) & (SDMMC_R6_GENERAL_UNKNOWN_ERROR | SDMMC_R6_ILLEGAL_CMD | SDMMC_R6_COM_CRC_FAILED)) return false;
+
+ *rca = SDIO_GetResponse(SDIO_RESP1) >> 16;
+ return true;
+}
+
+bool SDIO_GetCmdResp7() {
+ SDIO_WAIT(SDIO_STA_CCRCFAIL | SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT);
+
+ if (SDIO_GET_FLAG(SDIO_STA_CTIMEOUT)) {
+ SDIO_CLEAR_FLAG(SDIO_STA_CTIMEOUT);
+ return false;
+ }
+
+ if (SDIO_GET_FLAG(SDIO_STA_CMDREND)) { SDIO_CLEAR_FLAG(SDIO_STA_CMDREND); }
+ return true;
+}
+
+#endif // STM32_HIGH_DENSITY || STM32_XL_DENSITY
+#endif // ARDUINO_ARCH_STM32F1
diff --git a/Marlin/src/HAL/STM32F1/sdio.h b/Marlin/src/HAL/STM32F1/sdio.h
new file mode 100644
index 0000000000..8777299f01
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/sdio.h
@@ -0,0 +1,155 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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/MarlinConfig.h" // Allow pins/pins.h to override SDIO clock / retries
+
+#include
+#include
+
+// ------------------------
+// Defines
+// ------------------------
+
+#define SDMMC_CMD_GO_IDLE_STATE ((uint8_t)0) /* Resets the SD memory card. */
+#define SDMMC_CMD_ALL_SEND_CID ((uint8_t)2) /* Asks any card connected to the host to send the CID numbers on the CMD line. */
+#define SDMMC_CMD_SET_REL_ADDR ((uint8_t)3) /* Asks the card to publish a new relative address (RCA). */
+#define SDMMC_CMD_SEL_DESEL_CARD ((uint8_t)7) /* Selects the card by its own relative address and gets deselected by any other address */
+#define SDMMC_CMD_HS_SEND_EXT_CSD ((uint8_t)8) /* Sends SD Memory Card interface condition, which includes host supply voltage information and asks the card whether card supports voltage. */
+#define SDMMC_CMD_SEND_CSD ((uint8_t)9) /* Addressed card sends its card specific data (CSD) on the CMD line. */
+#define SDMMC_CMD_SEND_STATUS ((uint8_t)13) /*!< Addressed card sends its status register. */
+#define SDMMC_CMD_READ_SINGLE_BLOCK ((uint8_t)17) /* Reads single block of size selected by SET_BLOCKLEN in case of SDSC, and a block of fixed 512 bytes in case of SDHC and SDXC. */
+#define SDMMC_CMD_WRITE_SINGLE_BLOCK ((uint8_t)24) /* Writes single block of size selected by SET_BLOCKLEN in case of SDSC, and a block of fixed 512 bytes in case of SDHC and SDXC. */
+#define SDMMC_CMD_APP_CMD ((uint8_t)55) /* Indicates to the card that the next command is an application specific command rather than a standard command. */
+
+#define SDMMC_ACMD_APP_SD_SET_BUSWIDTH ((uint8_t)6) /* (ACMD6) Defines the data bus width to be used for data transfer. The allowed data bus widths are given in SCR register. */
+#define SDMMC_ACMD_SD_APP_OP_COND ((uint8_t)41) /* (ACMD41) Sends host capacity support information (HCS) and asks the accessed card to send its operating condition register (OCR) content in the response on the CMD line. */
+#define SDMMC_ACMD_SD_APP_SET_CLR_CARD_DETECT ((uint8_t)42) /* (ACMD42) Connect/Disconnect the 50 KOhm pull-up resistor on CD/DAT3 (pin 1) of the card */
+
+#define CMD0_GO_IDLE_STATE (uint16_t)(SDMMC_CMD_GO_IDLE_STATE | SDIO_CMD_WAIT_NO_RESP)
+#define CMD2_ALL_SEND_CID (uint16_t)(SDMMC_CMD_ALL_SEND_CID | SDIO_CMD_WAIT_LONG_RESP)
+#define CMD3_SET_REL_ADDR (uint16_t)(SDMMC_CMD_SET_REL_ADDR | SDIO_CMD_WAIT_SHORT_RESP)
+#define CMD7_SEL_DESEL_CARD (uint16_t)(SDMMC_CMD_SEL_DESEL_CARD | SDIO_CMD_WAIT_SHORT_RESP)
+#define CMD8_HS_SEND_EXT_CSD (uint16_t)(SDMMC_CMD_HS_SEND_EXT_CSD | SDIO_CMD_WAIT_SHORT_RESP)
+#define CMD9_SEND_CSD (uint16_t)(SDMMC_CMD_SEND_CSD | SDIO_CMD_WAIT_LONG_RESP)
+#define CMD13_SEND_STATUS (uint16_t)(SDMMC_CMD_SEND_STATUS | SDIO_CMD_WAIT_SHORT_RESP)
+#define CMD17_READ_SINGLE_BLOCK (uint16_t)(SDMMC_CMD_READ_SINGLE_BLOCK | SDIO_CMD_WAIT_SHORT_RESP)
+#define CMD24_WRITE_SINGLE_BLOCK (uint16_t)(SDMMC_CMD_WRITE_SINGLE_BLOCK | SDIO_CMD_WAIT_SHORT_RESP)
+#define CMD55_APP_CMD (uint16_t)(SDMMC_CMD_APP_CMD | SDIO_CMD_WAIT_SHORT_RESP)
+
+#define ACMD6_APP_SD_SET_BUSWIDTH (uint16_t)(SDMMC_ACMD_APP_SD_SET_BUSWIDTH | SDIO_CMD_WAIT_SHORT_RESP)
+#define ACMD41_SD_APP_OP_COND (uint16_t)(SDMMC_ACMD_SD_APP_OP_COND | SDIO_CMD_WAIT_SHORT_RESP)
+#define ACMD42_SD_APP_SET_CLR_CARD_DETECT (uint16_t)(SDMMC_ACMD_SD_APP_SET_CLR_CARD_DETECT | SDIO_CMD_WAIT_SHORT_RESP)
+
+
+#define SDMMC_ALLZERO 0x00000000U
+#define SDMMC_OCR_ERRORBITS 0xFDFFE008U
+
+#define SDMMC_R6_GENERAL_UNKNOWN_ERROR 0x00002000U
+#define SDMMC_R6_ILLEGAL_CMD 0x00004000U
+#define SDMMC_R6_COM_CRC_FAILED 0x00008000U
+
+#define SDMMC_VOLTAGE_WINDOW_SD 0x80100000U
+#define SDMMC_HIGH_CAPACITY 0x40000000U
+#define SDMMC_STD_CAPACITY 0x00000000U
+#define SDMMC_CHECK_PATTERN 0x000001AAU
+
+#define SDIO_TRANSFER_MODE_BLOCK 0x00000000U
+#define SDIO_DPSM_ENABLE 0x00000001U
+#define SDIO_TRANSFER_DIR_TO_CARD 0x00000000U
+#define SDIO_DATABLOCK_SIZE_512B 0x00000090U
+#define SDIO_TRANSFER_DIR_TO_SDIO 0x00000100U
+#define SDIO_DMA_ENABLE 0x00001000U
+
+#define CARD_V1_X 0x00000000U
+#define CARD_V2_X 0x00000001U
+#define CARD_SDSC 0x00000000U
+#define CARD_SDHC_SDXC 0x00000001U
+
+#define SDIO_RESP1 0
+#define SDIO_RESP2 1
+#define SDIO_RESP3 2
+#define SDIO_RESP4 3
+
+#define SDIO_GET_FLAG(__FLAG__) !!((SDIO->STA) & (__FLAG__))
+#define SDIO_CLEAR_FLAG(__FLAG__) (SDIO->ICR = (__FLAG__))
+
+#define SDMMC_MAX_VOLT_TRIAL 0x00000FFFU
+#define SDIO_CARD_TRANSFER 0x00000004U /* Card is in transfer state */
+#define SDIO_CARD_ERROR 0x000000FFU /* Card response Error */
+#define SDIO_CMDTIMEOUT 200U /* Command send and response timeout */
+#define SDIO_DATA_TIMEOUT 100U /* Read data transfer timeout */
+#define SDIO_WRITE_TIMEOUT 200U /* Write data transfer timeout */
+
+#ifndef SDIO_CLOCK
+ #define SDIO_CLOCK 18000000 /* 18 MHz */
+#endif
+
+#ifndef SDIO_READ_RETRIES
+ #define SDIO_READ_RETRIES 3
+#endif
+
+// ------------------------
+// Types
+// ------------------------
+
+typedef struct {
+ uint32_t CardType; // Card Type
+ uint32_t CardVersion; // Card version
+ uint32_t Class; // Class of the card class
+ uint32_t RelCardAdd; // Relative Card Address
+ uint32_t BlockNbr; // Card Capacity in blocks
+ uint32_t BlockSize; // One block size in bytes
+ uint32_t LogBlockNbr; // Card logical Capacity in blocks
+ uint32_t LogBlockSize; // Logical block size in bytes
+} SDIO_CardInfoTypeDef;
+
+// ------------------------
+// Public functions
+// ------------------------
+
+inline uint32_t SDIO_GetCardState();
+
+bool SDIO_CmdGoIdleState();
+bool SDIO_CmdSendCID();
+bool SDIO_CmdSetRelAdd(uint32_t *rca);
+bool SDIO_CmdSelDesel(uint32_t address);
+bool SDIO_CmdOperCond();
+bool SDIO_CmdSendCSD(uint32_t argument);
+bool SDIO_CmdSendStatus(uint32_t argument);
+bool SDIO_CmdReadSingleBlock(uint32_t address);
+bool SDIO_CmdWriteSingleBlock(uint32_t address);
+bool SDIO_CmdAppCommand(uint32_t rsa);
+
+bool SDIO_CmdAppSetBusWidth(uint32_t rsa, uint32_t argument);
+bool SDIO_CmdAppOperCommand(uint32_t sdType);
+bool SDIO_CmdAppSetClearCardDetect(uint32_t rsa);
+
+void SDIO_SendCommand(uint16_t command, uint32_t argument);
+uint8_t SDIO_GetCommandResponse();
+uint32_t SDIO_GetResponse(uint32_t response);
+bool SDIO_GetCmdError();
+bool SDIO_GetCmdResp1(uint8_t command);
+bool SDIO_GetCmdResp2();
+bool SDIO_GetCmdResp3();
+bool SDIO_GetCmdResp6(uint8_t command, uint32_t *rca);
+bool SDIO_GetCmdResp7();
diff --git a/Marlin/src/HAL/STM32F1/spi_pins.h b/Marlin/src/HAL/STM32F1/spi_pins.h
new file mode 100644
index 0000000000..3d3c8f8d2f
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/spi_pins.h
@@ -0,0 +1,57 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+/**
+ * HAL for stm32duino.com based on Libmaple and compatible (STM32F1)
+ */
+
+/**
+ * STM32F1 Default SPI Pins
+ *
+ * SS SCK MISO MOSI
+ * +-----------------------------+
+ * SPI1 | PA4 PA5 PA6 PA7 |
+ * SPI2 | PB12 PB13 PB14 PB15 |
+ * SPI3 | PA15 PB3 PB4 PB5 |
+ * +-----------------------------+
+ * Any pin can be used for Chip Select (SD_SS_PIN)
+ * SPI1 is enabled by default
+ */
+#ifndef SD_SCK_PIN
+ #define SD_SCK_PIN PA5
+#endif
+#ifndef SD_MISO_PIN
+ #define SD_MISO_PIN PA6
+#endif
+#ifndef SD_MOSI_PIN
+ #define SD_MOSI_PIN PA7
+#endif
+#ifndef SD_SS_PIN
+ #define SD_SS_PIN PA4
+#endif
+#undef SDSS
+#define SDSS SD_SS_PIN
+
+#ifndef SPI_DEVICE
+ #define SPI_DEVICE 1
+#endif
diff --git a/Marlin/src/HAL/STM32F1/tft/tft_fsmc.cpp b/Marlin/src/HAL/STM32F1/tft/tft_fsmc.cpp
new file mode 100644
index 0000000000..512e70cf3f
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/tft/tft_fsmc.cpp
@@ -0,0 +1,262 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if HAS_FSMC_TFT
+
+#include "tft_fsmc.h"
+#include
+#include
+#include
+
+LCD_CONTROLLER_TypeDef *TFT_FSMC::LCD;
+
+/**
+ * FSMC LCD IO
+ */
+#define __ASM __asm
+#define __STATIC_INLINE static inline
+
+__attribute__((always_inline)) __STATIC_INLINE void __DSB() {
+ __ASM volatile ("dsb 0xF":::"memory");
+}
+
+#define FSMC_CS_NE1 PD7
+
+#if ENABLED(STM32_XL_DENSITY)
+ #define FSMC_CS_NE2 PG9
+ #define FSMC_CS_NE3 PG10
+ #define FSMC_CS_NE4 PG12
+
+ #define FSMC_RS_A0 PF0
+ #define FSMC_RS_A1 PF1
+ #define FSMC_RS_A2 PF2
+ #define FSMC_RS_A3 PF3
+ #define FSMC_RS_A4 PF4
+ #define FSMC_RS_A5 PF5
+ #define FSMC_RS_A6 PF12
+ #define FSMC_RS_A7 PF13
+ #define FSMC_RS_A8 PF14
+ #define FSMC_RS_A9 PF15
+ #define FSMC_RS_A10 PG0
+ #define FSMC_RS_A11 PG1
+ #define FSMC_RS_A12 PG2
+ #define FSMC_RS_A13 PG3
+ #define FSMC_RS_A14 PG4
+ #define FSMC_RS_A15 PG5
+#endif
+
+#define FSMC_RS_A16 PD11
+#define FSMC_RS_A17 PD12
+#define FSMC_RS_A18 PD13
+#define FSMC_RS_A19 PE3
+#define FSMC_RS_A20 PE4
+#define FSMC_RS_A21 PE5
+#define FSMC_RS_A22 PE6
+#define FSMC_RS_A23 PE2
+
+#if ENABLED(STM32_XL_DENSITY)
+ #define FSMC_RS_A24 PG13
+ #define FSMC_RS_A25 PG14
+#endif
+
+/* Timing configuration */
+#define FSMC_ADDRESS_SETUP_TIME 15 // AddressSetupTime
+#define FSMC_DATA_SETUP_TIME 15 // DataSetupTime
+
+static uint8_t fsmcInit = 0;
+void TFT_FSMC::Init() {
+ uint8_t cs = FSMC_CS_PIN, rs = FSMC_RS_PIN;
+ uint32_t controllerAddress;
+
+ #if ENABLED(LCD_USE_DMA_FSMC)
+ dma_init(FSMC_DMA_DEV);
+ dma_disable(FSMC_DMA_DEV, FSMC_DMA_CHANNEL);
+ dma_set_priority(FSMC_DMA_DEV, FSMC_DMA_CHANNEL, DMA_PRIORITY_MEDIUM);
+ #endif
+
+ struct fsmc_nor_psram_reg_map* fsmcPsramRegion;
+
+ if (fsmcInit) return;
+ fsmcInit = 1;
+
+ switch (cs) {
+ case FSMC_CS_NE1: controllerAddress = (uint32_t)FSMC_NOR_PSRAM_REGION1; fsmcPsramRegion = FSMC_NOR_PSRAM1_BASE; break;
+ #if ENABLED(STM32_XL_DENSITY)
+ case FSMC_CS_NE2: controllerAddress = (uint32_t)FSMC_NOR_PSRAM_REGION2; fsmcPsramRegion = FSMC_NOR_PSRAM2_BASE; break;
+ case FSMC_CS_NE3: controllerAddress = (uint32_t)FSMC_NOR_PSRAM_REGION3; fsmcPsramRegion = FSMC_NOR_PSRAM3_BASE; break;
+ case FSMC_CS_NE4: controllerAddress = (uint32_t)FSMC_NOR_PSRAM_REGION4; fsmcPsramRegion = FSMC_NOR_PSRAM4_BASE; break;
+ #endif
+ default: return;
+ }
+
+ #define _ORADDR(N) controllerAddress |= (_BV32(N) - 2)
+
+ switch (rs) {
+ #if ENABLED(STM32_XL_DENSITY)
+ case FSMC_RS_A0: _ORADDR( 1); break;
+ case FSMC_RS_A1: _ORADDR( 2); break;
+ case FSMC_RS_A2: _ORADDR( 3); break;
+ case FSMC_RS_A3: _ORADDR( 4); break;
+ case FSMC_RS_A4: _ORADDR( 5); break;
+ case FSMC_RS_A5: _ORADDR( 6); break;
+ case FSMC_RS_A6: _ORADDR( 7); break;
+ case FSMC_RS_A7: _ORADDR( 8); break;
+ case FSMC_RS_A8: _ORADDR( 9); break;
+ case FSMC_RS_A9: _ORADDR(10); break;
+ case FSMC_RS_A10: _ORADDR(11); break;
+ case FSMC_RS_A11: _ORADDR(12); break;
+ case FSMC_RS_A12: _ORADDR(13); break;
+ case FSMC_RS_A13: _ORADDR(14); break;
+ case FSMC_RS_A14: _ORADDR(15); break;
+ case FSMC_RS_A15: _ORADDR(16); break;
+ #endif
+ case FSMC_RS_A16: _ORADDR(17); break;
+ case FSMC_RS_A17: _ORADDR(18); break;
+ case FSMC_RS_A18: _ORADDR(19); break;
+ case FSMC_RS_A19: _ORADDR(20); break;
+ case FSMC_RS_A20: _ORADDR(21); break;
+ case FSMC_RS_A21: _ORADDR(22); break;
+ case FSMC_RS_A22: _ORADDR(23); break;
+ case FSMC_RS_A23: _ORADDR(24); break;
+ #if ENABLED(STM32_XL_DENSITY)
+ case FSMC_RS_A24: _ORADDR(25); break;
+ case FSMC_RS_A25: _ORADDR(26); break;
+ #endif
+ default: return;
+ }
+
+ rcc_clk_enable(RCC_FSMC);
+
+ gpio_set_mode(GPIOD, 14, GPIO_AF_OUTPUT_PP); // FSMC_D00
+ gpio_set_mode(GPIOD, 15, GPIO_AF_OUTPUT_PP); // FSMC_D01
+ gpio_set_mode(GPIOD, 0, GPIO_AF_OUTPUT_PP); // FSMC_D02
+ gpio_set_mode(GPIOD, 1, GPIO_AF_OUTPUT_PP); // FSMC_D03
+ gpio_set_mode(GPIOE, 7, GPIO_AF_OUTPUT_PP); // FSMC_D04
+ gpio_set_mode(GPIOE, 8, GPIO_AF_OUTPUT_PP); // FSMC_D05
+ gpio_set_mode(GPIOE, 9, GPIO_AF_OUTPUT_PP); // FSMC_D06
+ gpio_set_mode(GPIOE, 10, GPIO_AF_OUTPUT_PP); // FSMC_D07
+ gpio_set_mode(GPIOE, 11, GPIO_AF_OUTPUT_PP); // FSMC_D08
+ gpio_set_mode(GPIOE, 12, GPIO_AF_OUTPUT_PP); // FSMC_D09
+ gpio_set_mode(GPIOE, 13, GPIO_AF_OUTPUT_PP); // FSMC_D10
+ gpio_set_mode(GPIOE, 14, GPIO_AF_OUTPUT_PP); // FSMC_D11
+ gpio_set_mode(GPIOE, 15, GPIO_AF_OUTPUT_PP); // FSMC_D12
+ gpio_set_mode(GPIOD, 8, GPIO_AF_OUTPUT_PP); // FSMC_D13
+ gpio_set_mode(GPIOD, 9, GPIO_AF_OUTPUT_PP); // FSMC_D14
+ gpio_set_mode(GPIOD, 10, GPIO_AF_OUTPUT_PP); // FSMC_D15
+
+ gpio_set_mode(GPIOD, 4, GPIO_AF_OUTPUT_PP); // FSMC_NOE
+ gpio_set_mode(GPIOD, 5, GPIO_AF_OUTPUT_PP); // FSMC_NWE
+
+ gpio_set_mode(PIN_MAP[cs].gpio_device, PIN_MAP[cs].gpio_bit, GPIO_AF_OUTPUT_PP); //FSMC_CS_NEx
+ gpio_set_mode(PIN_MAP[rs].gpio_device, PIN_MAP[rs].gpio_bit, GPIO_AF_OUTPUT_PP); //FSMC_RS_Ax
+
+ fsmcPsramRegion->BCR = FSMC_BCR_WREN | FSMC_BCR_MTYP_SRAM | FSMC_BCR_MWID_16BITS | FSMC_BCR_MBKEN;
+ fsmcPsramRegion->BTR = (FSMC_DATA_SETUP_TIME << 8) | FSMC_ADDRESS_SETUP_TIME;
+
+ afio_remap(AFIO_REMAP_FSMC_NADV);
+
+ LCD = (LCD_CONTROLLER_TypeDef*)controllerAddress;
+}
+
+void TFT_FSMC::Transmit(uint16_t Data) {
+ LCD->RAM = Data;
+ __DSB();
+}
+
+void TFT_FSMC::WriteReg(uint16_t Reg) {
+ LCD->REG = Reg;
+ __DSB();
+}
+
+uint32_t TFT_FSMC::GetID() {
+ uint32_t id;
+ WriteReg(0x0000);
+ id = LCD->RAM;
+
+ if (id == 0)
+ id = ReadID(LCD_READ_ID);
+ if ((id & 0xFFFF) == 0 || (id & 0xFFFF) == 0xFFFF)
+ id = ReadID(LCD_READ_ID4);
+ if ((id & 0xFF00) == 0 && (id & 0xFF) != 0)
+ id = ReadID(LCD_READ_ID4);
+ return id;
+}
+
+ uint32_t TFT_FSMC::ReadID(uint16_t Reg) {
+ uint32_t id;
+ WriteReg(Reg);
+ id = LCD->RAM; // dummy read
+ id = Reg << 24;
+ id |= (LCD->RAM & 0x00FF) << 16;
+ id |= (LCD->RAM & 0x00FF) << 8;
+ id |= LCD->RAM & 0x00FF;
+ return id;
+ }
+
+bool TFT_FSMC::isBusy() {
+ #define __IS_DMA_CONFIGURED(__DMAx__, __CHx__) (dma_channel_regs(__DMAx__, __CHx__)->CPAR != 0)
+
+ if (!__IS_DMA_CONFIGURED(FSMC_DMA_DEV, FSMC_DMA_CHANNEL)) return false;
+
+ // Check if DMA transfer error or transfer complete flags are set
+ if ((dma_get_isr_bits(FSMC_DMA_DEV, FSMC_DMA_CHANNEL) & (DMA_ISR_TCIF | DMA_ISR_TEIF)) == 0) return true;
+
+ __DSB();
+ Abort();
+ return false;
+}
+
+void TFT_FSMC::Abort() {
+ dma_channel_reg_map *channel_regs = dma_channel_regs(FSMC_DMA_DEV, FSMC_DMA_CHANNEL);
+
+ dma_disable(FSMC_DMA_DEV, FSMC_DMA_CHANNEL); // Abort DMA transfer if any
+
+ // Deconfigure DMA
+ channel_regs->CCR = 0U;
+ channel_regs->CNDTR = 0U;
+ channel_regs->CMAR = 0U;
+ channel_regs->CPAR = 0U;
+}
+
+void TFT_FSMC::TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) {
+ // TODO: HAL STM32 uses DMA2_Channel1 for FSMC on STM32F1
+ dma_setup_transfer(FSMC_DMA_DEV, FSMC_DMA_CHANNEL, Data, DMA_SIZE_16BITS, &LCD->RAM, DMA_SIZE_16BITS, DMA_MEM_2_MEM | MemoryIncrease);
+ dma_set_num_transfers(FSMC_DMA_DEV, FSMC_DMA_CHANNEL, Count);
+ dma_clear_isr_bits(FSMC_DMA_DEV, FSMC_DMA_CHANNEL);
+ dma_enable(FSMC_DMA_DEV, FSMC_DMA_CHANNEL);
+}
+
+void TFT_FSMC::Transmit(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) {
+ #if defined(FSMC_DMA_DEV) && defined(FSMC_DMA_CHANNEL)
+ dma_setup_transfer(FSMC_DMA_DEV, FSMC_DMA_CHANNEL, Data, DMA_SIZE_16BITS, &LCD->RAM, DMA_SIZE_16BITS, DMA_MEM_2_MEM | MemoryIncrease);
+ dma_set_num_transfers(FSMC_DMA_DEV, FSMC_DMA_CHANNEL, Count);
+ dma_clear_isr_bits(FSMC_DMA_DEV, FSMC_DMA_CHANNEL);
+ dma_enable(FSMC_DMA_DEV, FSMC_DMA_CHANNEL);
+
+ while ((dma_get_isr_bits(FSMC_DMA_DEV, FSMC_DMA_CHANNEL) & (DMA_CCR_TEIE | DMA_CCR_TCIE)) == 0) {}
+ Abort();
+ #endif
+}
+
+#endif // HAS_FSMC_TFT
diff --git a/Marlin/src/HAL/STM32F1/tft/tft_fsmc.h b/Marlin/src/HAL/STM32F1/tft/tft_fsmc.h
new file mode 100644
index 0000000000..8d26f6eac0
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/tft/tft_fsmc.h
@@ -0,0 +1,77 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#ifndef LCD_READ_ID
+ #define LCD_READ_ID 0x04 // Read display identification information (0xD3 on ILI9341)
+#endif
+#ifndef LCD_READ_ID4
+ #define LCD_READ_ID4 0xD3 // Read display identification information (0xD3 on ILI9341)
+#endif
+
+#include
+
+#define DATASIZE_8BIT DMA_SIZE_8BITS
+#define DATASIZE_16BIT DMA_SIZE_16BITS
+#define TFT_IO_DRIVER TFT_FSMC
+#define DMA_MAX_SIZE 0xFFFF
+
+#define DMA_PINC_ENABLE DMA_PINC_MODE
+#define DMA_PINC_DISABLE 0
+
+typedef struct {
+ __IO uint16_t REG;
+ __IO uint16_t RAM;
+} LCD_CONTROLLER_TypeDef;
+
+class TFT_FSMC {
+ private:
+ static LCD_CONTROLLER_TypeDef *LCD;
+
+ static uint32_t ReadID(uint16_t Reg);
+ static void Transmit(uint16_t Data);
+ static void Transmit(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count);
+ static void TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count);
+
+ public:
+ static void Init();
+ static uint32_t GetID();
+ static bool isBusy();
+ static void Abort();
+
+ static void DataTransferBegin(uint16_t DataWidth = DATASIZE_16BIT) {};
+ static void DataTransferEnd() {};
+
+ static void WriteData(uint16_t Data) { Transmit(Data); }
+ static void WriteReg(uint16_t Reg);
+
+ static void WriteSequence_DMA(uint16_t *Data, uint16_t Count) { TransmitDMA(DMA_PINC_ENABLE, Data, Count); }
+ static void WriteMultiple_DMA(uint16_t Color, uint16_t Count) { static uint16_t Data; Data = Color; TransmitDMA(DMA_PINC_DISABLE, &Data, Count); }
+
+ static void WriteSequence(uint16_t *Data, uint16_t Count) { Transmit(DMA_PINC_ENABLE, Data, Count); }
+ static void WriteMultiple(uint16_t Color, uint32_t Count) {
+ while (Count > 0) {
+ Transmit(DMA_PINC_DISABLE, &Color, Count > DMA_MAX_SIZE ? DMA_MAX_SIZE : Count);
+ Count = Count > DMA_MAX_SIZE ? Count - DMA_MAX_SIZE : 0;
+ }
+ }
+};
diff --git a/Marlin/src/HAL/STM32F1/tft/tft_spi.cpp b/Marlin/src/HAL/STM32F1/tft/tft_spi.cpp
new file mode 100644
index 0000000000..bb495d5f58
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/tft/tft_spi.cpp
@@ -0,0 +1,167 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if HAS_SPI_TFT
+
+#include "tft_spi.h"
+
+SPIClass TFT_SPI::SPIx(TFT_SPI_DEVICE);
+
+void TFT_SPI::Init() {
+ #if PIN_EXISTS(TFT_RESET)
+ OUT_WRITE(TFT_RESET_PIN, HIGH);
+ delay(100);
+ #endif
+
+ #if PIN_EXISTS(TFT_BACKLIGHT)
+ OUT_WRITE(TFT_BACKLIGHT_PIN, HIGH);
+ #endif
+
+ OUT_WRITE(TFT_DC_PIN, HIGH);
+ OUT_WRITE(TFT_CS_PIN, HIGH);
+
+ /**
+ * STM32F1 APB2 = 72MHz, APB1 = 36MHz, max SPI speed of this MCU if 18Mhz
+ * STM32F1 has 3 SPI ports, SPI1 in APB2, SPI2/SPI3 in APB1
+ * so the minimum prescale of SPI1 is DIV4, SPI2/SPI3 is DIV2
+ */
+ #if TFT_SPI_DEVICE == 1
+ #define SPI_CLOCK_MAX SPI_CLOCK_DIV4
+ #else
+ #define SPI_CLOCK_MAX SPI_CLOCK_DIV2
+ #endif
+ uint8_t clock;
+ uint8_t spiRate = SPI_FULL_SPEED;
+ switch (spiRate) {
+ case SPI_FULL_SPEED: clock = SPI_CLOCK_MAX ; break;
+ case SPI_HALF_SPEED: clock = SPI_CLOCK_DIV4 ; break;
+ case SPI_QUARTER_SPEED: clock = SPI_CLOCK_DIV8 ; break;
+ case SPI_EIGHTH_SPEED: clock = SPI_CLOCK_DIV16; break;
+ case SPI_SPEED_5: clock = SPI_CLOCK_DIV32; break;
+ case SPI_SPEED_6: clock = SPI_CLOCK_DIV64; break;
+ default: clock = SPI_CLOCK_DIV2; // Default from the SPI library
+ }
+ SPIx.setModule(TFT_SPI_DEVICE);
+ SPIx.setClockDivider(clock);
+ SPIx.setBitOrder(MSBFIRST);
+ SPIx.setDataMode(SPI_MODE0);
+}
+
+void TFT_SPI::DataTransferBegin(uint16_t DataSize) {
+ SPIx.setDataSize(DataSize);
+ SPIx.begin();
+ WRITE(TFT_CS_PIN, LOW);
+}
+
+#ifdef TFT_DEFAULT_DRIVER
+ #include "../../../lcd/tft_io/tft_ids.h"
+#endif
+
+uint32_t TFT_SPI::GetID() {
+ uint32_t id;
+ id = ReadID(LCD_READ_ID);
+ if ((id & 0xFFFF) == 0 || (id & 0xFFFF) == 0xFFFF) {
+ id = ReadID(LCD_READ_ID4);
+ #ifdef TFT_DEFAULT_DRIVER
+ if ((id & 0xFFFF) == 0 || (id & 0xFFFF) == 0xFFFF)
+ id = TFT_DEFAULT_DRIVER;
+ #endif
+ }
+ return id;
+}
+
+uint32_t TFT_SPI::ReadID(uint16_t Reg) {
+ #if !PIN_EXISTS(TFT_MISO)
+ return 0;
+ #else
+ uint8_t d = 0;
+ uint32_t data = 0;
+ SPIx.setClockDivider(SPI_CLOCK_DIV16);
+ DataTransferBegin(DATASIZE_8BIT);
+ WriteReg(Reg);
+
+ LOOP_L_N(i, 4) {
+ SPIx.read((uint8_t*)&d, 1);
+ data = (data << 8) | d;
+ }
+
+ DataTransferEnd();
+ SPIx.setClockDivider(SPI_CLOCK_MAX);
+
+ return data >> 7;
+ #endif
+}
+
+bool TFT_SPI::isBusy() {
+ #define __IS_DMA_CONFIGURED(__DMAx__, __CHx__) (dma_channel_regs(__DMAx__, __CHx__)->CPAR != 0)
+
+ if (!__IS_DMA_CONFIGURED(DMAx, DMA_CHx)) return false;
+
+ if (dma_get_isr_bits(DMAx, DMA_CHx) & DMA_ISR_TEIF) {
+ // You should not be here - DMA transfer error flag is set
+ // Abort DMA transfer and release SPI
+ }
+ else {
+ // Check if DMA transfer completed flag is set
+ if (!(dma_get_isr_bits(DMAx, DMA_CHx) & DMA_ISR_TCIF)) return true;
+ // Check if SPI TX butter is empty and SPI is idle
+ if (!(SPIdev->regs->SR & SPI_SR_TXE) || (SPIdev->regs->SR & SPI_SR_BSY)) return true;
+ }
+
+ Abort();
+ return false;
+}
+
+void TFT_SPI::Abort() {
+ dma_channel_reg_map *channel_regs = dma_channel_regs(DMAx, DMA_CHx);
+
+ dma_disable(DMAx, DMA_CHx); // Abort DMA transfer if any
+ spi_tx_dma_disable(SPIdev);
+
+ // Deconfigure DMA
+ channel_regs->CCR = 0U;
+ channel_regs->CNDTR = 0U;
+ channel_regs->CMAR = 0U;
+ channel_regs->CPAR = 0U;
+
+ DataTransferEnd();
+}
+
+void TFT_SPI::Transmit(uint16_t Data) { SPIx.send(Data); }
+
+void TFT_SPI::TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) {
+ DataTransferBegin();
+ SPIx.dmaSendAsync(Data, Count, MemoryIncrease == DMA_MINC_ENABLE);
+
+ TERN_(TFT_SHARED_SPI, while (isBusy()));
+}
+
+void TFT_SPI::Transmit(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) {
+ WRITE(TFT_DC_PIN, HIGH);
+ DataTransferBegin();
+ SPIx.dmaSend(Data, Count, MemoryIncrease == DMA_MINC_ENABLE);
+ DataTransferEnd();
+}
+
+#endif // HAS_SPI_TFT
diff --git a/Marlin/src/HAL/STM32F1/tft/tft_spi.h b/Marlin/src/HAL/STM32F1/tft/tft_spi.h
new file mode 100644
index 0000000000..2bda8c21f7
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/tft/tft_spi.h
@@ -0,0 +1,96 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include "../../../inc/MarlinConfig.h"
+
+#include
+
+#define IS_SPI(N) (BOARD_NR_SPI >= N && (TFT_SCK_PIN == BOARD_SPI##N##_SCK_PIN) && (TFT_MOSI_PIN == BOARD_SPI##N##_MOSI_PIN) && (TFT_MISO_PIN == BOARD_SPI##N##_MISO_PIN))
+#if IS_SPI(1)
+ #define TFT_SPI_DEVICE 1
+ #define SPIdev SPI1
+ #define DMAx DMA1
+ #define DMA_CHx DMA_CH3
+#elif IS_SPI(2)
+ #define TFT_SPI_DEVICE 2
+ #define SPIdev SPI2
+ #define DMAx DMA1
+ #define DMA_CHx DMA_CH5
+#elif IS_SPI(3)
+ #define TFT_SPI_DEVICE 3
+ #define SPIdev SPI3
+ #define DMAx DMA2
+ #define DMA_CHx DMA_CH2
+#else
+ #error "Invalid TFT SPI configuration."
+#endif
+#undef IS_SPI
+
+#ifndef LCD_READ_ID
+ #define LCD_READ_ID 0x04 // Read display identification information (0xD3 on ILI9341)
+#endif
+#ifndef LCD_READ_ID4
+ #define LCD_READ_ID4 0xD3 // Read display identification information (0xD3 on ILI9341)
+#endif
+
+#define DATASIZE_8BIT DATA_SIZE_8BIT
+#define DATASIZE_16BIT DATA_SIZE_16BIT
+#define TFT_IO_DRIVER TFT_SPI
+#define DMA_MAX_SIZE 0xFFFF
+
+#define DMA_MINC_ENABLE DMA_MINC_MODE
+#define DMA_MINC_DISABLE 0
+
+class TFT_SPI {
+private:
+ static uint32_t ReadID(uint16_t Reg);
+ static void Transmit(uint16_t Data);
+ static void Transmit(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count);
+ static void TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count);
+
+public:
+ static SPIClass SPIx;
+
+ static void Init();
+ static uint32_t GetID();
+ static bool isBusy();
+ static void Abort();
+
+ static void DataTransferBegin(uint16_t DataWidth = DATA_SIZE_16BIT);
+ static void DataTransferEnd() { WRITE(TFT_CS_PIN, HIGH); SPIx.end(); };
+ static void DataTransferAbort();
+
+ static void WriteData(uint16_t Data) { Transmit(Data); }
+ static void WriteReg(uint16_t Reg) { WRITE(TFT_DC_PIN, LOW); Transmit(Reg); WRITE(TFT_DC_PIN, HIGH); }
+
+ static void WriteSequence_DMA(uint16_t *Data, uint16_t Count) { TransmitDMA(DMA_MINC_ENABLE, Data, Count); }
+ static void WriteMultiple_DMA(uint16_t Color, uint16_t Count) { static uint16_t Data; Data = Color; TransmitDMA(DMA_MINC_DISABLE, &Data, Count); }
+
+ static void WriteSequence(uint16_t *Data, uint16_t Count) { Transmit(DMA_MINC_ENABLE, Data, Count); }
+ static void WriteMultiple(uint16_t Color, uint32_t Count) {
+ while (Count > 0) {
+ Transmit(DMA_MINC_DISABLE, &Color, Count > DMA_MAX_SIZE ? DMA_MAX_SIZE : Count);
+ Count = Count > DMA_MAX_SIZE ? Count - DMA_MAX_SIZE : 0;
+ }
+ }
+};
diff --git a/Marlin/src/HAL/STM32F1/tft/xpt2046.cpp b/Marlin/src/HAL/STM32F1/tft/xpt2046.cpp
new file mode 100644
index 0000000000..ac9ad072aa
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/tft/xpt2046.cpp
@@ -0,0 +1,144 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if HAS_TFT_XPT2046 || HAS_RES_TOUCH_BUTTONS
+
+#include "xpt2046.h"
+#include
+
+uint16_t delta(uint16_t a, uint16_t b) { return a > b ? a - b : b - a; }
+
+#if ENABLED(TOUCH_BUTTONS_HW_SPI)
+ #include
+
+ SPIClass XPT2046::SPIx(TOUCH_BUTTONS_HW_SPI_DEVICE);
+
+ static void touch_spi_init(uint8_t spiRate) {
+ /**
+ * STM32F1 APB2 = 72MHz, APB1 = 36MHz, max SPI speed of this MCU if 18Mhz
+ * STM32F1 has 3 SPI ports, SPI1 in APB2, SPI2/SPI3 in APB1
+ * so the minimum prescale of SPI1 is DIV4, SPI2/SPI3 is DIV2
+ */
+ uint8_t clock;
+ switch (spiRate) {
+ case SPI_FULL_SPEED: clock = SPI_CLOCK_DIV4; break;
+ case SPI_HALF_SPEED: clock = SPI_CLOCK_DIV4; break;
+ case SPI_QUARTER_SPEED: clock = SPI_CLOCK_DIV8; break;
+ case SPI_EIGHTH_SPEED: clock = SPI_CLOCK_DIV16; break;
+ case SPI_SPEED_5: clock = SPI_CLOCK_DIV32; break;
+ case SPI_SPEED_6: clock = SPI_CLOCK_DIV64; break;
+ default: clock = SPI_CLOCK_DIV2; // Default from the SPI library
+ }
+ XPT2046::SPIx.setModule(TOUCH_BUTTONS_HW_SPI_DEVICE);
+ XPT2046::SPIx.setClockDivider(clock);
+ XPT2046::SPIx.setBitOrder(MSBFIRST);
+ XPT2046::SPIx.setDataMode(SPI_MODE0);
+ }
+#endif // TOUCH_BUTTONS_HW_SPI
+
+void XPT2046::Init() {
+ SET_INPUT(TOUCH_MISO_PIN);
+ SET_OUTPUT(TOUCH_MOSI_PIN);
+ SET_OUTPUT(TOUCH_SCK_PIN);
+ OUT_WRITE(TOUCH_CS_PIN, HIGH);
+
+ #if PIN_EXISTS(TOUCH_INT)
+ // Optional Pendrive interrupt pin
+ SET_INPUT(TOUCH_INT_PIN);
+ #endif
+
+ TERN_(TOUCH_BUTTONS_HW_SPI, touch_spi_init(SPI_SPEED_6));
+
+ // Read once to enable pendrive status pin
+ getRawData(XPT2046_X);
+}
+
+bool XPT2046::isTouched() {
+ return isBusy() ? false : (
+ #if PIN_EXISTS(TOUCH_INT)
+ READ(TOUCH_INT_PIN) != HIGH
+ #else
+ getRawData(XPT2046_Z1) >= XPT2046_Z1_THRESHOLD
+ #endif
+ );
+}
+
+bool XPT2046::getRawPoint(int16_t *x, int16_t *y) {
+ if (isBusy()) return false;
+ if (!isTouched()) return false;
+ *x = getRawData(XPT2046_X);
+ *y = getRawData(XPT2046_Y);
+ return isTouched();
+}
+
+uint16_t XPT2046::getRawData(const XPTCoordinate coordinate) {
+ uint16_t data[3];
+
+ DataTransferBegin();
+ TERN_(TOUCH_BUTTONS_HW_SPI, SPIx.begin());
+
+ for (uint16_t i = 0; i < 3 ; i++) {
+ IO(coordinate);
+ data[i] = (IO() << 4) | (IO() >> 4);
+ }
+
+ TERN_(TOUCH_BUTTONS_HW_SPI, SPIx.end());
+ DataTransferEnd();
+
+ uint16_t delta01 = delta(data[0], data[1]),
+ delta02 = delta(data[0], data[2]),
+ delta12 = delta(data[1], data[2]);
+
+ if (delta01 > delta02 || delta01 > delta12)
+ data[delta02 > delta12 ? 0 : 1] = data[2];
+
+ return (data[0] + data[1]) >> 1;
+}
+
+uint16_t XPT2046::IO(uint16_t data) {
+ return TERN(TOUCH_BUTTONS_HW_SPI, HardwareIO, SoftwareIO)(data);
+}
+
+#if ENABLED(TOUCH_BUTTONS_HW_SPI)
+ uint16_t XPT2046::HardwareIO(uint16_t data) {
+ uint16_t result = SPIx.transfer(data);
+ return result;
+ }
+#endif
+
+uint16_t XPT2046::SoftwareIO(uint16_t data) {
+ uint16_t result = 0;
+
+ for (uint8_t j = 0x80; j; j >>= 1) {
+ WRITE(TOUCH_SCK_PIN, LOW);
+ WRITE(TOUCH_MOSI_PIN, data & j ? HIGH : LOW);
+ if (READ(TOUCH_MISO_PIN)) result |= j;
+ WRITE(TOUCH_SCK_PIN, HIGH);
+ }
+ WRITE(TOUCH_SCK_PIN, LOW);
+
+ return result;
+}
+
+#endif // HAS_TFT_XPT2046
diff --git a/Marlin/src/HAL/STM32F1/tft/xpt2046.h b/Marlin/src/HAL/STM32F1/tft/xpt2046.h
new file mode 100644
index 0000000000..7c456cf00e
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/tft/xpt2046.h
@@ -0,0 +1,83 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(TOUCH_BUTTONS_HW_SPI)
+ #include
+#endif
+
+#ifndef TOUCH_MISO_PIN
+ #define TOUCH_MISO_PIN SD_MISO_PIN
+#endif
+#ifndef TOUCH_MOSI_PIN
+ #define TOUCH_MOSI_PIN SD_MOSI_PIN
+#endif
+#ifndef TOUCH_SCK_PIN
+ #define TOUCH_SCK_PIN SD_SCK_PIN
+#endif
+#ifndef TOUCH_CS_PIN
+ #define TOUCH_CS_PIN SD_SS_PIN
+#endif
+#ifndef TOUCH_INT_PIN
+ #define TOUCH_INT_PIN -1
+#endif
+
+#define XPT2046_DFR_MODE 0x00
+#define XPT2046_SER_MODE 0x04
+#define XPT2046_CONTROL 0x80
+
+enum XPTCoordinate : uint8_t {
+ XPT2046_X = 0x10 | XPT2046_CONTROL | XPT2046_DFR_MODE,
+ XPT2046_Y = 0x50 | XPT2046_CONTROL | XPT2046_DFR_MODE,
+ XPT2046_Z1 = 0x30 | XPT2046_CONTROL | XPT2046_DFR_MODE,
+ XPT2046_Z2 = 0x40 | XPT2046_CONTROL | XPT2046_DFR_MODE,
+};
+
+#ifndef XPT2046_Z1_THRESHOLD
+ #define XPT2046_Z1_THRESHOLD 10
+#endif
+
+class XPT2046 {
+private:
+ static bool isBusy() { return false; }
+
+ static uint16_t getRawData(const XPTCoordinate coordinate);
+ static bool isTouched();
+
+ static void DataTransferBegin() { WRITE(TOUCH_CS_PIN, LOW); };
+ static void DataTransferEnd() { WRITE(TOUCH_CS_PIN, HIGH); };
+ #if ENABLED(TOUCH_BUTTONS_HW_SPI)
+ static uint16_t HardwareIO(uint16_t data);
+ #endif
+ static uint16_t SoftwareIO(uint16_t data);
+ static uint16_t IO(uint16_t data = 0);
+
+public:
+ #if ENABLED(TOUCH_BUTTONS_HW_SPI)
+ static SPIClass SPIx;
+ #endif
+
+ static void Init();
+ static bool getRawPoint(int16_t *x, int16_t *y);
+};
diff --git a/Marlin/src/HAL/STM32F1/timers.cpp b/Marlin/src/HAL/STM32F1/timers.cpp
new file mode 100644
index 0000000000..112c730b9a
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/timers.cpp
@@ -0,0 +1,183 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
+ *
+ * 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 .
+ *
+ */
+
+/**
+ * HAL for stm32duino.com based on Libmaple and compatible (STM32F1)
+ */
+
+#ifdef __STM32F1__
+
+#include "../../inc/MarlinConfig.h"
+
+// ------------------------
+// Local defines
+// ------------------------
+
+// ------------------------
+// Public functions
+// ------------------------
+
+/**
+ * Timer_clock1: Prescaler 2 -> 36 MHz
+ * Timer_clock2: Prescaler 8 -> 9 MHz
+ * Timer_clock3: Prescaler 32 -> 2.25 MHz
+ * Timer_clock4: Prescaler 128 -> 562.5 kHz
+ */
+
+/**
+ * TODO: Calculate Timer prescale value, so we get the 32bit to adjust
+ */
+
+void HAL_timer_set_interrupt_priority(uint_fast8_t timer_num, uint_fast8_t priority) {
+ nvic_irq_num irq_num;
+ switch (timer_num) {
+ case 1: irq_num = NVIC_TIMER1_CC; break;
+ case 2: irq_num = NVIC_TIMER2; break;
+ case 3: irq_num = NVIC_TIMER3; break;
+ case 4: irq_num = NVIC_TIMER4; break;
+ case 5: irq_num = NVIC_TIMER5; break;
+ #ifdef STM32_HIGH_DENSITY
+ // 6 & 7 are basic timers, avoid them
+ case 8: irq_num = NVIC_TIMER8_CC; break;
+ #endif
+ default:
+ /**
+ * This should never happen. Add a Sanitycheck for timer number.
+ * Should be a general timer since basic timers have no CC channels.
+ */
+ return;
+ }
+
+ nvic_irq_set_priority(irq_num, priority);
+}
+
+void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) {
+ /**
+ * Give the Stepper ISR a higher priority (lower number)
+ * so it automatically preempts the Temperature ISR.
+ */
+
+ switch (timer_num) {
+ case MF_TIMER_STEP:
+ timer_pause(STEP_TIMER_DEV);
+ timer_set_mode(STEP_TIMER_DEV, STEP_TIMER_CHAN, TIMER_OUTPUT_COMPARE); // counter
+ timer_set_count(STEP_TIMER_DEV, 0);
+ timer_set_prescaler(STEP_TIMER_DEV, (uint16_t)(STEPPER_TIMER_PRESCALE - 1));
+ timer_set_reload(STEP_TIMER_DEV, 0xFFFF);
+ timer_oc_set_mode(STEP_TIMER_DEV, STEP_TIMER_CHAN, TIMER_OC_MODE_FROZEN, TIMER_OC_NO_PRELOAD); // no output pin change
+ timer_set_compare(STEP_TIMER_DEV, STEP_TIMER_CHAN, _MIN(hal_timer_t(HAL_TIMER_TYPE_MAX), (STEPPER_TIMER_RATE) / frequency));
+ timer_no_ARR_preload_ARPE(STEP_TIMER_DEV); // Need to be sure no preload on ARR register
+ timer_attach_interrupt(STEP_TIMER_DEV, STEP_TIMER_CHAN, stepTC_Handler);
+ HAL_timer_set_interrupt_priority(MF_TIMER_STEP, STEP_TIMER_IRQ_PRIO);
+ timer_generate_update(STEP_TIMER_DEV);
+ timer_resume(STEP_TIMER_DEV);
+ break;
+ case MF_TIMER_TEMP:
+ timer_pause(TEMP_TIMER_DEV);
+ timer_set_mode(TEMP_TIMER_DEV, TEMP_TIMER_CHAN, TIMER_OUTPUT_COMPARE);
+ timer_set_count(TEMP_TIMER_DEV, 0);
+ timer_set_prescaler(TEMP_TIMER_DEV, (uint16_t)(TEMP_TIMER_PRESCALE - 1));
+ timer_set_reload(TEMP_TIMER_DEV, 0xFFFF);
+ timer_set_compare(TEMP_TIMER_DEV, TEMP_TIMER_CHAN, _MIN(hal_timer_t(HAL_TIMER_TYPE_MAX), (F_CPU) / (TEMP_TIMER_PRESCALE) / frequency));
+ timer_attach_interrupt(TEMP_TIMER_DEV, TEMP_TIMER_CHAN, tempTC_Handler);
+ HAL_timer_set_interrupt_priority(MF_TIMER_TEMP, TEMP_TIMER_IRQ_PRIO);
+ timer_generate_update(TEMP_TIMER_DEV);
+ timer_resume(TEMP_TIMER_DEV);
+ break;
+ }
+}
+
+void HAL_timer_enable_interrupt(const uint8_t timer_num) {
+ switch (timer_num) {
+ case MF_TIMER_STEP: ENABLE_STEPPER_DRIVER_INTERRUPT(); break;
+ case MF_TIMER_TEMP: ENABLE_TEMPERATURE_INTERRUPT(); break;
+ }
+}
+
+void HAL_timer_disable_interrupt(const uint8_t timer_num) {
+ switch (timer_num) {
+ case MF_TIMER_STEP: DISABLE_STEPPER_DRIVER_INTERRUPT(); break;
+ case MF_TIMER_TEMP: DISABLE_TEMPERATURE_INTERRUPT(); break;
+ }
+}
+
+static inline bool HAL_timer_irq_enabled(const timer_dev * const dev, const uint8_t interrupt) {
+ return bool(*bb_perip(&(dev->regs).gen->DIER, interrupt));
+}
+
+bool HAL_timer_interrupt_enabled(const uint8_t timer_num) {
+ switch (timer_num) {
+ case MF_TIMER_STEP: return HAL_timer_irq_enabled(STEP_TIMER_DEV, STEP_TIMER_CHAN);
+ case MF_TIMER_TEMP: return HAL_timer_irq_enabled(TEMP_TIMER_DEV, TEMP_TIMER_CHAN);
+ }
+ return false;
+}
+
+timer_dev* HAL_get_timer_dev(int number) {
+ switch (number) {
+ #if STM32_HAVE_TIMER(1)
+ case 1: return &timer1;
+ #endif
+ #if STM32_HAVE_TIMER(2)
+ case 2: return &timer2;
+ #endif
+ #if STM32_HAVE_TIMER(3)
+ case 3: return &timer3;
+ #endif
+ #if STM32_HAVE_TIMER(4)
+ case 4: return &timer4;
+ #endif
+ #if STM32_HAVE_TIMER(5)
+ case 5: return &timer5;
+ #endif
+ #if STM32_HAVE_TIMER(6)
+ case 6: return &timer6;
+ #endif
+ #if STM32_HAVE_TIMER(7)
+ case 7: return &timer7;
+ #endif
+ #if STM32_HAVE_TIMER(8)
+ case 8: return &timer8;
+ #endif
+ #if STM32_HAVE_TIMER(9)
+ case 9: return &timer9;
+ #endif
+ #if STM32_HAVE_TIMER(10)
+ case 10: return &timer10;
+ #endif
+ #if STM32_HAVE_TIMER(11)
+ case 11: return &timer11;
+ #endif
+ #if STM32_HAVE_TIMER(12)
+ case 12: return &timer12;
+ #endif
+ #if STM32_HAVE_TIMER(13)
+ case 13: return &timer13;
+ #endif
+ #if STM32_HAVE_TIMER(14)
+ case 14: return &timer14;
+ #endif
+ default: return nullptr;
+ }
+}
+
+#endif // __STM32F1__
diff --git a/Marlin/src/HAL/STM32F1/timers.h b/Marlin/src/HAL/STM32F1/timers.h
new file mode 100644
index 0000000000..0cd807fc84
--- /dev/null
+++ b/Marlin/src/HAL/STM32F1/timers.h
@@ -0,0 +1,201 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2017 Victor Perez
+ *
+ * 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
+
+/**
+ * HAL for stm32duino.com based on Libmaple and compatible (STM32F1)
+ */
+
+#include "../../inc/MarlinConfig.h"
+#include "HAL.h"
+
+#include
+
+// ------------------------
+// Defines
+// ------------------------
+
+/**
+ * TODO: Check and confirm what timer we will use for each Temps and stepper driving.
+ * We should probable drive temps with PWM.
+ */
+
+typedef uint16_t hal_timer_t;
+#define HAL_TIMER_TYPE_MAX 0xFFFF
+
+#define HAL_TIMER_RATE uint32_t(F_CPU) // frequency of timers peripherals
+
+#ifndef STEP_TIMER_CHAN
+ #define STEP_TIMER_CHAN 1 // Channel of the timer to use for compare and interrupts
+#endif
+#ifndef TEMP_TIMER_CHAN
+ #define TEMP_TIMER_CHAN 1 // Channel of the timer to use for compare and interrupts
+#endif
+
+/**
+ * Note: Timers may be used by platforms and libraries
+ *
+ * FAN PWMs:
+ * With FAN_SOFT_PWM disabled the Temperature class uses
+ * FANx_PIN timers to generate FAN PWM signals.
+ *
+ * Speaker:
+ * When SPEAKER is enabled, one timer is allocated by maple/tone.cpp.
+ * - If BEEPER_PIN has a timer channel (and USE_PIN_TIMER is
+ * defined in tone.cpp) it uses the pin's own timer.
+ * - Otherwise it uses Timer 8 on boards with STM32_HIGH_DENSITY
+ * or Timer 4 on other boards.
+ */
+#ifndef MF_TIMER_STEP
+ #if defined(MCU_STM32F103CB) || defined(MCU_STM32F103C8)
+ #define MF_TIMER_STEP 4 // For C8/CB boards, use timer 4
+ #else
+ #define MF_TIMER_STEP 5 // for other boards, five is fine.
+ #endif
+#endif
+#ifndef MF_TIMER_PULSE
+ #define MF_TIMER_PULSE MF_TIMER_STEP
+#endif
+#ifndef MF_TIMER_TEMP
+ #define MF_TIMER_TEMP 2 // Timer Index for Temperature
+ //#define MF_TIMER_TEMP 4 // 2->4, Timer 2 for Stepper Current PWM
+#endif
+
+#if MB(BTT_SKR_MINI_E3_V1_0, BTT_SKR_E3_DIP, BTT_SKR_MINI_E3_V1_2, MKS_ROBIN_LITE, MKS_ROBIN_E3D, MKS_ROBIN_E3)
+ // SKR Mini E3 boards use PA8 as FAN_PIN, so TIMER 1 is used for Fan PWM.
+ #ifdef STM32_HIGH_DENSITY
+ #define MF_TIMER_SERVO0 8 // tone.cpp uses Timer 4
+ #else
+ #define MF_TIMER_SERVO0 3 // tone.cpp uses Timer 8
+ #endif
+#else
+ #define MF_TIMER_SERVO0 1 // SERVO0 or BLTOUCH
+#endif
+
+#define STEP_TIMER_IRQ_PRIO 2
+#define TEMP_TIMER_IRQ_PRIO 3
+#define SERVO0_TIMER_IRQ_PRIO 1
+
+#define TEMP_TIMER_PRESCALE 1000 // prescaler for setting Temp timer, 72Khz
+#define TEMP_TIMER_FREQUENCY 1000 // temperature interrupt frequency
+
+#define STEPPER_TIMER_PRESCALE 18 // prescaler for setting stepper timer, 4Mhz
+#define STEPPER_TIMER_RATE (HAL_TIMER_RATE / STEPPER_TIMER_PRESCALE) // frequency of stepper timer
+#define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) // stepper timer ticks per µs
+
+#define PULSE_TIMER_RATE STEPPER_TIMER_RATE // frequency of pulse timer
+#define PULSE_TIMER_PRESCALE STEPPER_TIMER_PRESCALE
+#define PULSE_TIMER_TICKS_PER_US STEPPER_TIMER_TICKS_PER_US
+
+timer_dev* HAL_get_timer_dev(int number);
+#define TIMER_DEV(num) HAL_get_timer_dev(num)
+#define STEP_TIMER_DEV TIMER_DEV(MF_TIMER_STEP)
+#define TEMP_TIMER_DEV TIMER_DEV(MF_TIMER_TEMP)
+
+#define ENABLE_STEPPER_DRIVER_INTERRUPT() timer_enable_irq(STEP_TIMER_DEV, STEP_TIMER_CHAN)
+#define DISABLE_STEPPER_DRIVER_INTERRUPT() timer_disable_irq(STEP_TIMER_DEV, STEP_TIMER_CHAN)
+#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(MF_TIMER_STEP)
+
+#define ENABLE_TEMPERATURE_INTERRUPT() timer_enable_irq(TEMP_TIMER_DEV, TEMP_TIMER_CHAN)
+#define DISABLE_TEMPERATURE_INTERRUPT() timer_disable_irq(TEMP_TIMER_DEV, TEMP_TIMER_CHAN)
+
+#define HAL_timer_get_count(timer_num) timer_get_count(TIMER_DEV(timer_num))
+
+// TODO change this
+
+#ifndef HAL_TEMP_TIMER_ISR
+ #define HAL_TEMP_TIMER_ISR() extern "C" void tempTC_Handler()
+#endif
+#ifndef HAL_STEP_TIMER_ISR
+ #define HAL_STEP_TIMER_ISR() extern "C" void stepTC_Handler()
+#endif
+
+extern "C" {
+ void tempTC_Handler();
+ void stepTC_Handler();
+}
+
+// ------------------------
+// Public Variables
+// ------------------------
+
+//static HardwareTimer StepperTimer(MF_TIMER_STEP);
+//static HardwareTimer TempTimer(MF_TIMER_TEMP);
+
+// ------------------------
+// Public functions
+// ------------------------
+
+void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency);
+void HAL_timer_enable_interrupt(const uint8_t timer_num);
+void HAL_timer_disable_interrupt(const uint8_t timer_num);
+bool HAL_timer_interrupt_enabled(const uint8_t timer_num);
+
+/**
+ * NOTE: By default libmaple sets ARPE = 1, which means the Auto reload register is preloaded (will only update with an update event)
+ * Thus we have to pause the timer, update the value, refresh, resume the timer.
+ * That seems like a big waste of time and may be better to change the timer config to ARPE = 0, so ARR can be updated any time.
+ * We are using a Channel in each timer in Capture/Compare mode. We could also instead use the Time Update Event Interrupt, but need to disable ARPE
+ * so we can change the ARR value on the fly (without calling refresh), and not get an interrupt right there because we caused an UEV.
+ * This mode pretty much makes 2 timers unusable for PWM since they have their counts updated all the time on ISRs.
+ * The way Marlin manages timer interrupts doesn't make for an efficient usage in STM32F1
+ * Todo: Look at that possibility later.
+ */
+
+FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, const hal_timer_t compare) {
+ switch (timer_num) {
+ case MF_TIMER_STEP:
+ // NOTE: WE have set ARPE = 0, which means the Auto reload register is not preloaded
+ // and there is no need to use any compare, as in the timer mode used, setting ARR to the compare value
+ // will result in exactly the same effect, ie triggering an interrupt, and on top, set counter to 0
+ timer_set_reload(STEP_TIMER_DEV, compare); // We reload direct ARR as needed during counting up
+ break;
+ case MF_TIMER_TEMP:
+ timer_set_compare(TEMP_TIMER_DEV, TEMP_TIMER_CHAN, compare);
+ break;
+ }
+}
+
+FORCE_INLINE static void HAL_timer_isr_prologue(const uint8_t timer_num) {
+ switch (timer_num) {
+ case MF_TIMER_STEP:
+ // No counter to clear
+ timer_generate_update(STEP_TIMER_DEV);
+ return;
+ case MF_TIMER_TEMP:
+ timer_set_count(TEMP_TIMER_DEV, 0);
+ timer_generate_update(TEMP_TIMER_DEV);
+ return;
+ }
+}
+
+#define HAL_timer_isr_epilogue(T) NOOP
+
+// No command is available in framework to turn off ARPE bit, which is turned on by default in libmaple.
+// Needed here to reset ARPE=0 for stepper timer
+FORCE_INLINE static void timer_no_ARR_preload_ARPE(timer_dev *dev) {
+ bb_peri_set_bit(&(dev->regs).gen->CR1, TIMER_CR1_ARPE_BIT, 0);
+}
+
+void HAL_timer_set_interrupt_priority(uint_fast8_t timer_num, uint_fast8_t priority);
+
+#define TIMER_OC_NO_PRELOAD 0 // Need to disable preload also on compare registers.
diff --git a/Marlin/src/HAL/platforms.h b/Marlin/src/HAL/platforms.h
new file mode 100644
index 0000000000..488980ce09
--- /dev/null
+++ b/Marlin/src/HAL/platforms.h
@@ -0,0 +1,57 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#define XSTR(V...) #V
+
+#ifdef __AVR__
+ #define HAL_PATH(PATH, NAME) XSTR(PATH/AVR/NAME)
+#elif defined(ARDUINO_ARCH_SAM)
+ #define HAL_PATH(PATH, NAME) XSTR(PATH/DUE/NAME)
+#elif defined(__MK20DX256__)
+ #define HAL_PATH(PATH, NAME) XSTR(PATH/TEENSY31_32/NAME)
+#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)
+ #define HAL_PATH(PATH, NAME) XSTR(PATH/TEENSY35_36/NAME)
+#elif defined(__IMXRT1062__)
+ #define HAL_PATH(PATH, NAME) XSTR(PATH/TEENSY40_41/NAME)
+#elif defined(TARGET_LPC1768)
+ #define HAL_PATH(PATH, NAME) XSTR(PATH/LPC1768/NAME)
+#elif defined(__STM32F1__) || defined(TARGET_STM32F1)
+ #define HAL_PATH(PATH, NAME) XSTR(PATH/STM32F1/NAME)
+#elif defined(ARDUINO_ARCH_STM32)
+ #ifndef HAL_STM32
+ #define HAL_STM32
+ #endif
+ #define HAL_PATH(PATH, NAME) XSTR(PATH/STM32/NAME)
+#elif defined(ARDUINO_ARCH_ESP32)
+ #define HAL_PATH(PATH, NAME) XSTR(PATH/ESP32/NAME)
+#elif defined(__PLAT_LINUX__)
+ #define HAL_PATH(PATH, NAME) XSTR(PATH/LINUX/NAME)
+#elif defined(__PLAT_NATIVE_SIM__)
+ #define HAL_PATH(PATH, NAME) XSTR(PATH/NATIVE_SIM/NAME)
+#elif defined(__SAMD51__)
+ #define HAL_PATH(PATH, NAME) XSTR(PATH/SAMD51/NAME)
+#elif defined(__SAMD21__)
+ #define HAL_PATH(PATH, NAME) XSTR(PATH/SAMD21/NAME)
+#else
+ #error "Unsupported Platform!"
+#endif
diff --git a/Marlin/src/HAL/shared/Delay.cpp b/Marlin/src/HAL/shared/Delay.cpp
new file mode 100644
index 0000000000..c64376d25d
--- /dev/null
+++ b/Marlin/src/HAL/shared/Delay.cpp
@@ -0,0 +1,178 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "Delay.h"
+
+#include "../../inc/MarlinConfig.h"
+
+#if defined(__arm__) || defined(__thumb__)
+
+ static uint32_t ASM_CYCLES_PER_ITERATION = 4; // Initial bet which will be adjusted in calibrate_delay_loop
+
+ // Simple assembler loop counting down
+ void delay_asm(uint32_t cy) {
+ cy = _MAX(cy / ASM_CYCLES_PER_ITERATION, 1U); // Zero is forbidden here
+ __asm__ __volatile__(
+ A(".syntax unified") // is to prevent CM0,CM1 non-unified syntax
+ L("1")
+ A("subs %[cnt],#1")
+ A("bne 1b")
+ : [cnt]"+r"(cy) // output: +r means input+output
+ : // input:
+ : "cc" // clobbers:
+ );
+ }
+
+ // We can't use CMSIS since it's not available on all platform, so fallback to hardcoded register values
+ #define HW_REG(X) *(volatile uint32_t *)(X)
+ #define _DWT_CTRL 0xE0001000
+ #define _DWT_CYCCNT 0xE0001004 // CYCCNT is 32bits, takes 37s or so to wrap.
+ #define _DEM_CR 0xE000EDFC
+ #define _LAR 0xE0001FB0
+
+ // Use hardware cycle counter instead, it's much safer
+ void delay_dwt(uint32_t count) {
+ // Reuse the ASM_CYCLES_PER_ITERATION variable to avoid wasting another useless variable
+ uint32_t start = HW_REG(_DWT_CYCCNT) - ASM_CYCLES_PER_ITERATION, elapsed;
+ do {
+ elapsed = HW_REG(_DWT_CYCCNT) - start;
+ } while (elapsed < count);
+ }
+
+ // Pointer to asm function, calling the functions has a ~20 cycles overhead
+ DelayImpl DelayCycleFnc = delay_asm;
+
+ void calibrate_delay_loop() {
+ // Check if we have a working DWT implementation in the CPU (see https://developer.arm.com/documentation/ddi0439/b/Data-Watchpoint-and-Trace-Unit/DWT-Programmers-Model)
+ if (!HW_REG(_DWT_CTRL)) {
+ // No DWT present, so fallback to plain old ASM nop counting
+ // Unfortunately, we don't exactly know how many iteration it'll take to decrement a counter in a loop
+ // It depends on the CPU architecture, the code current position (flash vs SRAM)
+ // So, instead of wild guessing and making mistake, instead
+ // compute it once for all
+ ASM_CYCLES_PER_ITERATION = 1;
+ // We need to fetch some reference clock before waiting
+ cli();
+ uint32_t start = micros();
+ delay_asm(1000); // On a typical CPU running in MHz, waiting 1000 "unknown cycles" means it'll take between 1ms to 6ms, that's perfectly acceptable
+ uint32_t end = micros();
+ sei();
+ uint32_t expectedCycles = (end - start) * ((F_CPU) / 1000000UL); // Convert microseconds to cycles
+ // Finally compute the right scale
+ ASM_CYCLES_PER_ITERATION = (uint32_t)(expectedCycles / 1000);
+
+ // No DWT present, likely a Cortex M0 so NOP counting is our best bet here
+ DelayCycleFnc = delay_asm;
+ }
+ else {
+ // Enable DWT counter
+ // From https://stackoverflow.com/a/41188674/1469714
+ HW_REG(_DEM_CR) = HW_REG(_DEM_CR) | 0x01000000; // Enable trace
+ #if __CORTEX_M == 7
+ HW_REG(_LAR) = 0xC5ACCE55; // Unlock access to DWT registers, see https://developer.arm.com/documentation/ihi0029/e/ section B2.3.10
+ #endif
+ HW_REG(_DWT_CYCCNT) = 0; // Clear DWT cycle counter
+ HW_REG(_DWT_CTRL) = HW_REG(_DWT_CTRL) | 1; // Enable DWT cycle counter
+
+ // Then calibrate the constant offset from the counter
+ ASM_CYCLES_PER_ITERATION = 0;
+ uint32_t s = HW_REG(_DWT_CYCCNT);
+ uint32_t e = HW_REG(_DWT_CYCCNT); // (e - s) contains the number of cycle required to read the cycle counter
+ delay_dwt(0);
+ uint32_t f = HW_REG(_DWT_CYCCNT); // (f - e) contains the delay to call the delay function + the time to read the cycle counter
+ ASM_CYCLES_PER_ITERATION = (f - e) - (e - s);
+
+ // Use safer DWT function
+ DelayCycleFnc = delay_dwt;
+ }
+ }
+
+ #if ENABLED(MARLIN_DEV_MODE)
+ void dump_delay_accuracy_check() {
+ auto report_call_time = [](FSTR_P const name, FSTR_P const unit, const uint32_t cycles, const uint32_t total, const bool do_flush=true) {
+ SERIAL_ECHOPGM("Calling ");
+ SERIAL_ECHOF(name);
+ SERIAL_ECHOLNPGM(" for ", cycles);
+ SERIAL_ECHOF(unit);
+ SERIAL_ECHOLNPGM(" took: ", total);
+ SERIAL_CHAR(' ');
+ SERIAL_ECHOF(unit);
+ if (do_flush) SERIAL_FLUSHTX();
+ };
+
+ uint32_t s, e;
+
+ SERIAL_ECHOLNPGM("Computed delay calibration value: ", ASM_CYCLES_PER_ITERATION);
+ SERIAL_FLUSH();
+ // Display the results of the calibration above
+ constexpr uint32_t testValues[] = { 1, 5, 10, 20, 50, 100, 150, 200, 350, 500, 750, 1000 };
+ for (auto i : testValues) {
+ s = micros(); DELAY_US(i); e = micros();
+ report_call_time(F("delay"), F("us"), i, e - s);
+ }
+
+ if (HW_REG(_DWT_CTRL)) {
+ static FSTR_P cyc = F("cycles");
+ static FSTR_P dcd = F("DELAY_CYCLES directly ");
+
+ for (auto i : testValues) {
+ s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(i); e = HW_REG(_DWT_CYCCNT);
+ report_call_time(F("runtime delay"), cyc, i, e - s);
+ }
+
+ // Measure the delay to call a real function compared to a function pointer
+ s = HW_REG(_DWT_CYCCNT); delay_dwt(1); e = HW_REG(_DWT_CYCCNT);
+ report_call_time(F("delay_dwt"), cyc, 1, e - s);
+
+ s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES( 1); e = HW_REG(_DWT_CYCCNT);
+ report_call_time(dcd, cyc, 1, e - s, false);
+
+ s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES( 5); e = HW_REG(_DWT_CYCCNT);
+ report_call_time(dcd, cyc, 5, e - s, false);
+
+ s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(10); e = HW_REG(_DWT_CYCCNT);
+ report_call_time(dcd, cyc, 10, e - s, false);
+
+ s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(20); e = HW_REG(_DWT_CYCCNT);
+ report_call_time(dcd, cyc, 20, e - s, false);
+
+ s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(50); e = HW_REG(_DWT_CYCCNT);
+ report_call_time(dcd, cyc, 50, e - s, false);
+
+ s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(100); e = HW_REG(_DWT_CYCCNT);
+ report_call_time(dcd, cyc, 100, e - s, false);
+
+ s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(200); e = HW_REG(_DWT_CYCCNT);
+ report_call_time(dcd, cyc, 200, e - s, false);
+ }
+ }
+ #endif // MARLIN_DEV_MODE
+
+
+#else
+
+ void calibrate_delay_loop() {}
+ #if ENABLED(MARLIN_DEV_MODE)
+ void dump_delay_accuracy_check() { SERIAL_ECHOPGM("N/A on this platform"); }
+ #endif
+
+#endif
diff --git a/Marlin/src/HAL/shared/Delay.h b/Marlin/src/HAL/shared/Delay.h
new file mode 100644
index 0000000000..a6795a78ea
--- /dev/null
+++ b/Marlin/src/HAL/shared/Delay.h
@@ -0,0 +1,219 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include "../../inc/MarlinConfigPre.h"
+
+/**
+ * Busy wait delay cycles routines:
+ *
+ * DELAY_CYCLES(count): Delay execution in cycles
+ * DELAY_NS(count): Delay execution in nanoseconds
+ * DELAY_US(count): Delay execution in microseconds
+ */
+
+#include "../../core/macros.h"
+
+void calibrate_delay_loop();
+
+#if defined(__arm__) || defined(__thumb__)
+
+ // We want to have delay_cycle function with the lowest possible overhead, so we adjust at the function at runtime based on the current CPU best feature
+ typedef void (*DelayImpl)(uint32_t);
+ extern DelayImpl DelayCycleFnc;
+
+ // I've measured 36 cycles on my system to call the cycle waiting method, but it shouldn't change much to have a bit more margin, it only consume a bit more flash
+ #define TRIP_POINT_FOR_CALLING_FUNCTION 40
+
+ // A simple recursive template class that output exactly one 'nop' of code per recursion
+ template struct NopWriter {
+ FORCE_INLINE static void build() {
+ __asm__ __volatile__("nop");
+ NopWriter::build();
+ }
+ };
+ // End the loop
+ template <> struct NopWriter<0> { FORCE_INLINE static void build() {} };
+
+ namespace Private {
+ // Split recursing template in 2 different class so we don't reach the maximum template instantiation depth limit
+ template struct Helper {
+ FORCE_INLINE static void build() {
+ DelayCycleFnc(N - 2); // Approximative cost of calling the function (might be off by one or 2 cycles)
+ }
+ };
+
+ template struct Helper {
+ FORCE_INLINE static void build() {
+ NopWriter::build();
+ }
+ };
+
+ template <> struct Helper {
+ FORCE_INLINE static void build() {}
+ };
+
+ }
+ // Select a behavior based on the constexpr'ness of the parameter
+ // If called with a compile-time parameter, then write as many NOP as required to reach the asked cycle count
+ // (there is some tripping point here to start looping when it's more profitable than gruntly executing NOPs)
+ // If not called from a compile-time parameter, fallback to a runtime loop counting version instead
+ template
+ struct SmartDelay {
+ FORCE_INLINE SmartDelay(int) {
+ if (Cycles == 0) return;
+ Private::Helper::build();
+ }
+ };
+ // Runtime version below. There is no way this would run under less than ~TRIP_POINT_FOR_CALLING_FUNCTION cycles
+ template
+ struct SmartDelay {
+ FORCE_INLINE SmartDelay(int v) { DelayCycleFnc(v); }
+ };
+
+ #define DELAY_CYCLES(X) do { SmartDelay _smrtdly_X(X); } while(0)
+
+ #if GCC_VERSION <= 70000
+ #define DELAY_CYCLES_VAR(X) DelayCycleFnc(X)
+ #else
+ #define DELAY_CYCLES_VAR DELAY_CYCLES
+ #endif
+
+ // For delay in microseconds, no smart delay selection is required, directly call the delay function
+ // Teensy compiler is too old and does not accept smart delay compile-time / run-time selection correctly
+ #define DELAY_US(x) DelayCycleFnc((x) * ((F_CPU) / 1000000UL))
+
+#elif defined(__AVR__)
+ FORCE_INLINE static void __delay_up_to_3c(uint8_t cycles) {
+ switch (cycles) {
+ case 3:
+ __asm__ __volatile__(A("RJMP .+0") A("NOP"));
+ break;
+ case 2:
+ __asm__ __volatile__(A("RJMP .+0"));
+ break;
+ case 1:
+ __asm__ __volatile__(A("NOP"));
+ break;
+ }
+ }
+
+ // Delay in cycles
+ FORCE_INLINE static void DELAY_CYCLES(uint16_t cycles) {
+ if (__builtin_constant_p(cycles)) {
+ if (cycles <= 3) {
+ __delay_up_to_3c(cycles);
+ }
+ else if (cycles == 4) {
+ __delay_up_to_3c(2);
+ __delay_up_to_3c(2);
+ }
+ else {
+ cycles -= 1 + 4; // Compensate for the first LDI (1) and the first round (4)
+ __delay_up_to_3c(cycles % 4);
+
+ cycles /= 4;
+ // The following code burns [1 + 4 * (rounds+1)] cycles
+ uint16_t dummy;
+ __asm__ __volatile__(
+ // "manually" load counter from constants, otherwise the compiler may optimize this part away
+ A("LDI %A[rounds], %[l]") // 1c
+ A("LDI %B[rounds], %[h]") // 1c (compensating the non branching BRCC)
+ L("1")
+ A("SBIW %[rounds], 1") // 2c
+ A("BRCC 1b") // 2c when branching, else 1c (end of loop)
+ : // Outputs ...
+ [rounds] "=w" (dummy) // Restrict to a wo (=) 16 bit register pair (w)
+ : // Inputs ...
+ [l] "M" (cycles%256), // Restrict to 0..255 constant (M)
+ [h] "M" (cycles/256) // Restrict to 0..255 constant (M)
+ :// Clobbers ...
+ "cc" // Indicate we are modifying flags like Carry (cc)
+ );
+ }
+ }
+ else {
+ __asm__ __volatile__(
+ L("1")
+ A("SBIW %[cycles], 4") // 2c
+ A("BRCC 1b") // 2c when branching, else 1c (end of loop)
+ : [cycles] "+w" (cycles) // output: Restrict to a rw (+) 16 bit register pair (w)
+ : // input: -
+ : "cc" // clobbers: We are modifying flags like Carry (cc)
+ );
+ }
+ }
+
+ // Delay in microseconds
+ #define DELAY_US(x) DELAY_CYCLES((x) * ((F_CPU) / 1000000UL))
+
+ #define DELAY_CYCLES_VAR DELAY_CYCLES
+
+#elif defined(ESP32) || defined(__PLAT_LINUX__) || defined(__PLAT_NATIVE_SIM__)
+
+ // DELAY_CYCLES specified inside platform
+
+ // Delay in microseconds
+ #define DELAY_US(x) DELAY_CYCLES((x) * ((F_CPU) / 1000000UL))
+#else
+
+ #error "Unsupported MCU architecture"
+
+#endif
+
+/**************************************************************
+ * Delay in nanoseconds. Requires the F_CPU macro.
+ * These macros follow avr-libc delay conventions.
+ *
+ * For AVR there are three possible operation modes, due to its
+ * slower clock speeds and thus coarser delay resolution. For
+ * example, when F_CPU = 16000000 the resolution is 62.5ns.
+ *
+ * Round up (default)
+ * Round up the delay according to the CPU clock resolution.
+ * e.g., 100 will give a delay of 2 cycles (125ns).
+ *
+ * Round down (DELAY_NS_ROUND_DOWN)
+ * Round down the delay according to the CPU clock resolution.
+ * e.g., 100 will be rounded down to 1 cycle (62.5ns).
+ *
+ * Nearest (DELAY_NS_ROUND_CLOSEST)
+ * Round the delay to the nearest number of clock cycles.
+ * e.g., 165 will be rounded up to 3 cycles (187.5ns) because
+ * it's closer to the requested delay than 2 cycle (125ns).
+ */
+
+#ifndef __AVR__
+ #undef DELAY_NS_ROUND_DOWN
+ #undef DELAY_NS_ROUND_CLOSEST
+#endif
+
+#if ENABLED(DELAY_NS_ROUND_DOWN)
+ #define _NS_TO_CYCLES(x) ( (x) * ((F_CPU) / 1000000UL) / 1000UL) // floor
+#elif ENABLED(DELAY_NS_ROUND_CLOSEST)
+ #define _NS_TO_CYCLES(x) (((x) * ((F_CPU) / 1000000UL) + 500) / 1000UL) // round
+#else
+ #define _NS_TO_CYCLES(x) (((x) * ((F_CPU) / 1000000UL) + 999) / 1000UL) // "ceil"
+#endif
+
+#define DELAY_NS(x) DELAY_CYCLES(_NS_TO_CYCLES(x))
+#define DELAY_NS_VAR(x) DELAY_CYCLES_VAR(_NS_TO_CYCLES(x))
diff --git a/Marlin/src/HAL/shared/HAL.cpp b/Marlin/src/HAL/shared/HAL.cpp
new file mode 100644
index 0000000000..4d92aedd9a
--- /dev/null
+++ b/Marlin/src/HAL/shared/HAL.cpp
@@ -0,0 +1,36 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 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 .
+ *
+ */
+
+/**
+ * HAL/shared/HAL.cpp
+ */
+
+#include "../../inc/MarlinConfig.h"
+
+MarlinHAL hal;
+
+#if ENABLED(SOFT_RESET_VIA_SERIAL)
+
+ // Global for use by e_parser.h
+ void HAL_reboot() { hal.reboot(); }
+
+#endif
diff --git a/Marlin/src/HAL/shared/HAL_SPI.h b/Marlin/src/HAL/shared/HAL_SPI.h
new file mode 100644
index 0000000000..6611f9ec4e
--- /dev/null
+++ b/Marlin/src/HAL/shared/HAL_SPI.h
@@ -0,0 +1,93 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+/**
+ * HAL/shared/HAL_SPI.h
+ * Core Marlin definitions for SPI, implemented in the HALs
+ */
+
+#include "Marduino.h"
+#include
+
+/**
+ * SPI speed where 0 <= index <= 6
+ *
+ * Approximate rates :
+ *
+ * 0 : 8 - 10 MHz
+ * 1 : 4 - 5 MHz
+ * 2 : 2 - 2.5 MHz
+ * 3 : 1 - 1.25 MHz
+ * 4 : 500 - 625 kHz
+ * 5 : 250 - 312 kHz
+ * 6 : 125 - 156 kHz
+ *
+ * On AVR, actual speed is F_CPU/2^(1 + index).
+ * On other platforms, speed should be in range given above where possible.
+ */
+
+#define SPI_FULL_SPEED 0 // Set SCK to max rate
+#define SPI_HALF_SPEED 1 // Set SCK rate to half of max rate
+#define SPI_QUARTER_SPEED 2 // Set SCK rate to quarter of max rate
+#define SPI_EIGHTH_SPEED 3 // Set SCK rate to 1/8 of max rate
+#define SPI_SIXTEENTH_SPEED 4 // Set SCK rate to 1/16 of max rate
+#define SPI_SPEED_5 5 // Set SCK rate to 1/32 of max rate
+#define SPI_SPEED_6 6 // Set SCK rate to 1/64 of max rate
+
+//
+// Standard SPI functions
+//
+
+// Initialize SPI bus
+void spiBegin();
+
+// Configure SPI for specified SPI speed
+void spiInit(uint8_t spiRate);
+
+// Write single byte to SPI
+void spiSend(uint8_t b);
+
+// Read single byte from SPI
+uint8_t spiRec();
+
+// Read from SPI into buffer
+void spiRead(uint8_t *buf, uint16_t nbyte);
+
+// Write token and then write from 512 byte buffer to SPI (for SD card)
+void spiSendBlock(uint8_t token, const uint8_t *buf);
+
+// Begin SPI transaction, set clock, bit order, data mode
+void spiBeginTransaction(uint32_t spiClock, uint8_t bitOrder, uint8_t dataMode);
+
+//
+// Extended SPI functions taking a channel number (Hardware SPI only)
+//
+
+// Write single byte to specified SPI channel
+void spiSend(uint32_t chan, byte b);
+
+// Write buffer to specified SPI channel
+void spiSend(uint32_t chan, const uint8_t *buf, size_t n);
+
+// Read single byte from specified SPI channel
+uint8_t spiRec(uint32_t chan);
diff --git a/Marlin/src/HAL/shared/HAL_ST7920.h b/Marlin/src/HAL/shared/HAL_ST7920.h
new file mode 100644
index 0000000000..4e362f96ba
--- /dev/null
+++ b/Marlin/src/HAL/shared/HAL_ST7920.h
@@ -0,0 +1,36 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+/**
+ * HAL/ST7920.h
+ * For the HALs that provide direct access to the ST7920 display
+ * (bypassing U8G), it will allow the LIGHTWEIGHT_UI to operate.
+ */
+
+#if BOTH(HAS_MARLINUI_U8GLIB, LIGHTWEIGHT_UI)
+ void ST7920_cs();
+ void ST7920_ncs();
+ void ST7920_set_cmd();
+ void ST7920_set_dat();
+ void ST7920_write_byte(const uint8_t data);
+#endif
diff --git a/Marlin/src/HAL/shared/Marduino.h b/Marlin/src/HAL/shared/Marduino.h
new file mode 100644
index 0000000000..0e2a021a3c
--- /dev/null
+++ b/Marlin/src/HAL/shared/Marduino.h
@@ -0,0 +1,96 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+/**
+ * HAL/shared/Marduino.h
+ */
+
+#undef DISABLED // Redefined by ESP32
+#undef M_PI // Redefined by all
+#undef _BV // Redefined by some
+#undef SBI // Redefined by arduino/const_functions.h
+#undef CBI // Redefined by arduino/const_functions.h
+#undef sq // Redefined by teensy3/wiring.h
+#undef UNUSED // Redefined by stm32f4xx_hal_def.h
+
+#include // NOTE: If included earlier then this line is a NOOP
+
+#undef DISABLED
+#define DISABLED(V...) DO(DIS,&&,V)
+
+#undef _BV
+#define _BV(b) (1 << (b))
+#ifndef SBI
+ #define SBI(A,B) (A |= _BV(B))
+#endif
+#ifndef CBI
+ #define CBI(A,B) (A &= ~_BV(B))
+#endif
+
+#undef sq
+#define sq(x) ((x)*(x))
+
+#ifndef __AVR__
+ #ifndef strchr_P // Some platforms define a macro (DUE, teensy35)
+ inline const char* strchr_P(const char *s, int c) { return strchr(s,c); }
+ //#define strchr_P(s,c) strchr(s,c)
+ #endif
+
+ #ifndef snprintf_P
+ #define snprintf_P snprintf
+ #endif
+ #ifndef vsnprintf_P
+ #define vsnprintf_P vsnprintf
+ #endif
+#endif
+
+// Restart causes
+#define RST_POWER_ON 1
+#define RST_EXTERNAL 2
+#define RST_BROWN_OUT 4
+#define RST_WATCHDOG 8
+#define RST_JTAG 16
+#define RST_SOFTWARE 32
+#define RST_BACKUP 64
+
+#ifndef M_PI
+ #define M_PI 3.14159265358979323846f
+#endif
+
+// Remove compiler warning on an unused variable
+#ifndef UNUSED
+ #define UNUSED(x) ((void)(x))
+#endif
+
+#ifndef FORCE_INLINE
+ #define FORCE_INLINE __attribute__((always_inline)) inline
+#endif
+
+#include "progmem.h"
+
+class __FlashStringHelper;
+typedef const __FlashStringHelper* FSTR_P;
+#ifndef FPSTR
+ #define FPSTR(S) (reinterpret_cast(S))
+#endif
+#define FTOP(S) (reinterpret_cast(S))
diff --git a/Marlin/src/HAL/shared/MinSerial.cpp b/Marlin/src/HAL/shared/MinSerial.cpp
new file mode 100644
index 0000000000..2e718d83dc
--- /dev/null
+++ b/Marlin/src/HAL/shared/MinSerial.cpp
@@ -0,0 +1,33 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 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 "MinSerial.h"
+
+#if ENABLED(POSTMORTEM_DEBUGGING)
+
+void HAL_min_serial_init_default() {}
+void HAL_min_serial_out_default(char ch) { SERIAL_CHAR(ch); }
+void (*HAL_min_serial_init)() = &HAL_min_serial_init_default;
+void (*HAL_min_serial_out)(char) = &HAL_min_serial_out_default;
+
+bool MinSerial::force_using_default_output = false;
+
+#endif
diff --git a/Marlin/src/HAL/shared/MinSerial.h b/Marlin/src/HAL/shared/MinSerial.h
new file mode 100644
index 0000000000..3089b8aa06
--- /dev/null
+++ b/Marlin/src/HAL/shared/MinSerial.h
@@ -0,0 +1,79 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 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 "../../core/serial.h"
+#include
+
+// Serial stuff here
+// Inside an exception handler, the CPU state is not safe, we can't expect the handler to resume
+// and the software to continue. UART communication can't rely on later callback/interrupt as it might never happen.
+// So, you need to provide some method to send one byte to the usual UART with the interrupts disabled
+// By default, the method uses SERIAL_CHAR but it's 100% guaranteed to break (couldn't be worse than nothing...)7
+extern void (*HAL_min_serial_init)();
+extern void (*HAL_min_serial_out)(char ch);
+
+struct MinSerial {
+ static bool force_using_default_output;
+ // Serial output
+ static void TX(char ch) {
+ if (force_using_default_output)
+ SERIAL_CHAR(ch);
+ else
+ HAL_min_serial_out(ch);
+ }
+ // Send String through UART
+ static void TX(const char *s) { while (*s) TX(*s++); }
+ // Send a digit through UART
+ static void TXDigit(uint32_t d) {
+ if (d < 10) TX((char)(d+'0'));
+ else if (d < 16) TX((char)(d+'A'-10));
+ else TX('?');
+ }
+
+ // Send Hex number through UART
+ static void TXHex(uint32_t v) {
+ TX("0x");
+ for (uint8_t i = 0; i < 8; i++, v <<= 4)
+ TXDigit((v >> 28) & 0xF);
+ }
+
+ // Send Decimal number through UART
+ static void TXDec(uint32_t v) {
+ if (!v) {
+ TX('0');
+ return;
+ }
+
+ char nbrs[14];
+ char *p = &nbrs[0];
+ while (v != 0) {
+ *p++ = '0' + (v % 10);
+ v /= 10;
+ }
+ do {
+ p--;
+ TX(*p);
+ } while (p != &nbrs[0]);
+ }
+ static void init() { if (!force_using_default_output) HAL_min_serial_init(); }
+};
diff --git a/Marlin/src/HAL/shared/backtrace/backtrace.cpp b/Marlin/src/HAL/shared/backtrace/backtrace.cpp
new file mode 100644
index 0000000000..33e8e65154
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/backtrace.cpp
@@ -0,0 +1,106 @@
+/**
+ * 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 .
+ *
+ */
+#if defined(__arm__) || defined(__thumb__)
+
+#include "backtrace.h"
+#include "unwinder.h"
+#include "unwmemaccess.h"
+
+#include "../MinSerial.h"
+#include
+
+// Dump a backtrace entry
+static bool UnwReportOut(void *ctx, const UnwReport *bte) {
+ int *p = (int*)ctx;
+
+ (*p)++;
+
+ const uint32_t a = bte->address, f = bte->function;
+ MinSerial::TX('#'); MinSerial::TXDec(*p); MinSerial::TX(" : ");
+ MinSerial::TX(bte->name?:"unknown"); MinSerial::TX('@'); MinSerial::TXHex(f);
+ MinSerial::TX('+'); MinSerial::TXDec(a - f);
+ MinSerial::TX(" PC:"); MinSerial::TXHex(a);
+ MinSerial::TX('\n');
+ return true;
+}
+
+#ifdef UNW_DEBUG
+ void UnwPrintf(const char *format, ...) {
+ char dest[256];
+ va_list argptr;
+ va_start(argptr, format);
+ vsprintf(dest, format, argptr);
+ va_end(argptr);
+ MinSerial::TX(&dest[0]);
+ }
+#endif
+
+/* Table of function pointers for passing to the unwinder */
+static const UnwindCallbacks UnwCallbacks = {
+ UnwReportOut,
+ UnwReadW,
+ UnwReadH,
+ UnwReadB
+ #ifdef UNW_DEBUG
+ , UnwPrintf
+ #endif
+};
+
+// Perform a backtrace to the serial port
+void backtrace() {
+
+ unsigned long sp = 0, lr = 0, pc = 0;
+
+ // Capture the values of the registers to perform the traceback
+ __asm__ __volatile__ (
+ " mov %[lrv],lr\n"
+ " mov %[spv],sp\n"
+ " mov %[pcv],pc\n"
+ : [spv]"+r"( sp ),
+ [lrv]"+r"( lr ),
+ [pcv]"+r"( pc )
+ ::
+ );
+
+ backtrace_ex(sp, lr, pc);
+}
+
+void backtrace_ex(unsigned long sp, unsigned long lr, unsigned long pc) {
+ UnwindFrame btf;
+
+ // Fill the traceback structure
+ btf.sp = sp;
+ btf.fp = btf.sp;
+ btf.lr = lr;
+ btf.pc = pc | 1; // Force Thumb, as CORTEX only support it
+
+ // Perform a backtrace
+ MinSerial::TX("Backtrace:");
+ int ctr = 0;
+ UnwindStart(&btf, &UnwCallbacks, &ctr);
+}
+
+#else // !__arm__ && !__thumb__
+
+void backtrace() {}
+
+#endif // __arm__ || __thumb__
diff --git a/Marlin/src/HAL/shared/backtrace/backtrace.h b/Marlin/src/HAL/shared/backtrace/backtrace.h
new file mode 100644
index 0000000000..5d2ba601a0
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/backtrace.h
@@ -0,0 +1,28 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+// Perform a backtrace to the serial port
+void backtrace();
+
+// Perform a backtrace to the serial port
+void backtrace_ex(unsigned long sp, unsigned long lr, unsigned long pc);
diff --git a/Marlin/src/HAL/shared/backtrace/unwarm.cpp b/Marlin/src/HAL/shared/backtrace/unwarm.cpp
new file mode 100644
index 0000000000..e72a02e487
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/unwarm.cpp
@@ -0,0 +1,176 @@
+/***************************************************************************
+ * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
+ * Updated, adapted and several bug fixes on 2018 by Eduardo José Tagle
+ *
+ * This program is PUBLIC DOMAIN.
+ * This means that there is no copyright and anyone is able to take a copy
+ * for free and use it as they wish, with or without modifications, and in
+ * any context, commercially or otherwise. The only limitation is that I
+ * don't guarantee that the software is fit for any purpose or accept any
+ * liability for its use or misuse - this software is without warranty.
+ ***************************************************************************
+ * File Description: Utility functions and glue for ARM unwinding sub-modules.
+ **************************************************************************/
+
+#if defined(__arm__) || defined(__thumb__)
+
+#define MODULE_NAME "UNWARM"
+
+#include
+#include
+#include
+#include
+#include "unwarm.h"
+#include "unwarmmem.h"
+
+#ifdef UNW_DEBUG
+
+/**
+ * Printf wrapper.
+ * This is used such that alternative outputs for any output can be selected
+ * by modification of this wrapper function.
+ */
+void UnwPrintf(const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+}
+#endif
+
+/**
+ * Invalidate all general purpose registers.
+ */
+void UnwInvalidateRegisterFile(RegData *regFile) {
+ uint8_t t = 0;
+ do {
+ regFile[t].o = REG_VAL_INVALID;
+ t++;
+ } while (t < 13);
+}
+
+
+/**
+ * Initialize the data used for unwinding.
+ */
+void UnwInitState(UnwState * const state, /**< Pointer to structure to fill. */
+ const UnwindCallbacks *cb, /**< Callbacks. */
+ void *rptData, /**< Data to pass to report function. */
+ uint32_t pcValue, /**< PC at which to start unwinding. */
+ uint32_t spValue) { /**< SP at which to start unwinding. */
+
+ UnwInvalidateRegisterFile(state->regData);
+
+ /* Store the pointer to the callbacks */
+ state->cb = cb;
+ state->reportData = rptData;
+
+ /* Setup the SP and PC */
+ state->regData[13].v = spValue;
+ state->regData[13].o = REG_VAL_FROM_CONST;
+ state->regData[15].v = pcValue;
+ state->regData[15].o = REG_VAL_FROM_CONST;
+
+ UnwPrintd3("\nInitial: PC=0x%08x SP=0x%08x\n", pcValue, spValue);
+
+ /* Invalidate all memory addresses */
+ memset(state->memData.used, 0, sizeof(state->memData.used));
+}
+
+// Detect if function names are available
+static int __attribute__ ((noinline)) has_function_names() {
+ uint32_t flag_word = ((uint32_t*)(((uint32_t)(&has_function_names)) & (-4))) [-1];
+ return ((flag_word & 0xFF000000) == 0xFF000000) ? 1 : 0;
+}
+
+/**
+ * Call the report function to indicate some return address.
+ * This returns the value of the report function, which if true
+ * indicates that unwinding may continue.
+ */
+bool UnwReportRetAddr(UnwState * const state, uint32_t addr) {
+
+ UnwReport entry;
+
+ // We found two acceptable values.
+ entry.name = nullptr;
+ entry.address = addr & 0xFFFFFFFE; // Remove Thumb bit
+ entry.function = 0;
+
+ // If there are function names, try to solve name
+ if (has_function_names()) {
+
+ // Lets find the function name, if possible
+
+ // Align address to 4 bytes
+ uint32_t pf = addr & (-4);
+
+ // Scan backwards until we find the function name
+ uint32_t v;
+ while (state->cb->readW(pf-4,&v)) {
+
+ // Check if name descriptor is valid
+ if ((v & 0xFFFFFF00) == 0xFF000000 && (v & 0xFF) > 1) {
+ // Assume the name was found!
+ entry.name = ((const char*)pf) - 4 - (v & 0xFF);
+ entry.function = pf;
+ break;
+ }
+
+ // Go backwards to the previous word
+ pf -= 4;
+ }
+ }
+
+ /* Cast away const from reportData.
+ * The const is only to prevent the unw module modifying the data.
+ */
+ return state->cb->report((void *)state->reportData, &entry);
+}
+
+
+/**
+ * Write some register to memory.
+ * This will store some register and meta data onto the virtual stack.
+ * The address for the write
+ * \param state [in/out] The unwinding state.
+ * \param wAddr [in] The address at which to write the data.
+ * \param reg [in] The register to store.
+ * \return true if the write was successful, false otherwise.
+ */
+bool UnwMemWriteRegister(UnwState * const state, const uint32_t addr, const RegData * const reg) {
+ return UnwMemHashWrite(&state->memData, addr, reg->v, M_IsOriginValid(reg->o));
+}
+
+/**
+ * Read a register from memory.
+ * This will read a register from memory, and setup the meta data.
+ * If the register has been previously written to memory using
+ * UnwMemWriteRegister, the local hash will be used to return the
+ * value while respecting whether the data was valid or not. If the
+ * register was previously written and was invalid at that point,
+ * REG_VAL_INVALID will be returned in *reg.
+ * \param state [in] The unwinding state.
+ * \param addr [in] The address to read.
+ * \param reg [out] The result, containing the data value and the origin
+ * which will be REG_VAL_FROM_MEMORY, or REG_VAL_INVALID.
+ * \return true if the address could be read and *reg has been filled in.
+ * false is the data could not be read.
+ */
+bool UnwMemReadRegister(UnwState * const state, const uint32_t addr, RegData * const reg) {
+ bool tracked;
+
+ // Check if the value can be found in the hash
+ if (UnwMemHashRead(&state->memData, addr, ®->v, &tracked)) {
+ reg->o = tracked ? REG_VAL_FROM_MEMORY : REG_VAL_INVALID;
+ return true;
+ }
+ else if (state->cb->readW(addr, ®->v)) { // Not in the hash, so read from real memory
+ reg->o = REG_VAL_FROM_MEMORY;
+ return true;
+ }
+ else return false; // Not in the hash, and failed to read from memory
+}
+
+#endif // __arm__ || __thumb__
diff --git a/Marlin/src/HAL/shared/backtrace/unwarm.h b/Marlin/src/HAL/shared/backtrace/unwarm.h
new file mode 100644
index 0000000000..edae90650e
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/unwarm.h
@@ -0,0 +1,140 @@
+/***************************************************************************
+ * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
+ *
+ * This program is PUBLIC DOMAIN.
+ * This means that there is no copyright and anyone is able to take a copy
+ * for free and use it as they wish, with or without modifications, and in
+ * any context, commercially or otherwise. The only limitation is that I
+ * don't guarantee that the software is fit for any purpose or accept any
+ * liability for its use or misuse - this software is without warranty.
+ ***************************************************************************
+ * File Description: Internal interface between the ARM unwinding sub-modules.
+ **************************************************************************/
+
+#pragma once
+
+#include "unwinder.h"
+
+/** The maximum number of instructions to interpet in a function.
+ * Unwinding will be unconditionally stopped and UNWIND_EXHAUSTED returned
+ * if more than this number of instructions are interpreted in a single
+ * function without unwinding a stack frame. This prevents infinite loops
+ * or corrupted program memory from preventing unwinding from progressing.
+ */
+#define UNW_MAX_INSTR_COUNT 500
+
+/** The size of the hash used to track reads and writes to memory.
+ * This should be a prime value for efficiency.
+ */
+#define MEM_HASH_SIZE 31
+
+/***************************************************************************
+ * Type Definitions
+ **************************************************************************/
+
+typedef enum {
+ /** Invalid value. */
+ REG_VAL_INVALID = 0x00,
+ REG_VAL_FROM_STACK = 0x01,
+ REG_VAL_FROM_MEMORY = 0x02,
+ REG_VAL_FROM_CONST = 0x04,
+ REG_VAL_ARITHMETIC = 0x80
+} RegValOrigin;
+
+
+/** Type for tracking information about a register.
+ * This stores the register value, as well as other data that helps unwinding.
+ */
+typedef struct {
+
+ /** The value held in the register. */
+ uint32_t v;
+
+ /** The origin of the register value.
+ * This is used to track how the value in the register was loaded.
+ */
+ int o; /* (RegValOrigin) */
+} RegData;
+
+
+/** Structure used to track reads and writes to memory.
+ * This structure is used as a hash to store a small number of writes
+ * to memory.
+ */
+typedef struct {
+ /** Memory contents. */
+ uint32_t v[MEM_HASH_SIZE];
+
+ /** Address at which v[n] represents. */
+ uint32_t a[MEM_HASH_SIZE];
+
+ /** Indicates whether the data in v[n] and a[n] is occupied.
+ * Each bit represents one hash value.
+ */
+ uint8_t used[(MEM_HASH_SIZE + 7) / 8];
+
+ /** Indicates whether the data in v[n] is valid.
+ * This allows a[n] to be set, but for v[n] to be marked as invalid.
+ * Specifically this is needed for when an untracked register value
+ * is written to memory.
+ */
+ uint8_t tracked[(MEM_HASH_SIZE + 7) / 8];
+} MemData;
+
+
+/** Structure that is used to keep track of unwinding meta-data.
+ * This data is passed between all the unwinding functions.
+ */
+typedef struct {
+ /** The register values and meta-data. */
+ RegData regData[16];
+
+ /** Memory tracking data. */
+ MemData memData;
+
+ /** Pointer to the callback functions */
+ const UnwindCallbacks *cb;
+
+ /** Pointer to pass to the report function. */
+ const void *reportData;
+} UnwState;
+
+/***************************************************************************
+ * Macros
+ **************************************************************************/
+
+#define M_IsOriginValid(v) !!((v) & 0x7F)
+#define M_Origin2Str(v) ((v) ? "VALID" : "INVALID")
+
+#ifdef UNW_DEBUG
+#define UnwPrintd1(a) state->cb->printf(a)
+#define UnwPrintd2(a,b) state->cb->printf(a,b)
+#define UnwPrintd3(a,b,c) state->cb->printf(a,b,c)
+#define UnwPrintd4(a,b,c,d) state->cb->printf(a,b,c,d)
+#define UnwPrintd5(a,b,c,d,e) state->cb->printf(a,b,c,d,e)
+#define UnwPrintd6(a,b,c,d,e,f) state->cb->printf(a,b,c,d,e,f)
+#define UnwPrintd7(a,b,c,d,e,f,g) state->cb->printf(a,b,c,d,e,f,g)
+#define UnwPrintd8(a,b,c,d,e,f,g,h) state->cb->printf(a,b,c,d,e,f,g,h)
+#else
+#define UnwPrintd1(a)
+#define UnwPrintd2(a,b)
+#define UnwPrintd3(a,b,c)
+#define UnwPrintd4(a,b,c,d)
+#define UnwPrintd5(a,b,c,d,e)
+#define UnwPrintd6(a,b,c,d,e,f)
+#define UnwPrintd7(a,b,c,d,e,f,g)
+#define UnwPrintd8(a,b,c,d,e,f,g,h)
+#endif
+
+/***************************************************************************
+ * Function Prototypes
+ **************************************************************************/
+
+UnwResult UnwStartArm(UnwState * const state);
+UnwResult UnwStartThumb(UnwState * const state);
+void UnwInvalidateRegisterFile(RegData *regFile);
+void UnwInitState(UnwState * const state, const UnwindCallbacks *cb, void *rptData, uint32_t pcValue, uint32_t spValue);
+bool UnwReportRetAddr(UnwState * const state, uint32_t addr);
+bool UnwMemWriteRegister(UnwState * const state, const uint32_t addr, const RegData * const reg);
+bool UnwMemReadRegister(UnwState * const state, const uint32_t addr, RegData * const reg);
+void UnwMemHashGC(UnwState * const state);
diff --git a/Marlin/src/HAL/shared/backtrace/unwarm_arm.cpp b/Marlin/src/HAL/shared/backtrace/unwarm_arm.cpp
new file mode 100644
index 0000000000..decf74e6e9
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/unwarm_arm.cpp
@@ -0,0 +1,534 @@
+/***************************************************************************
+ * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
+ * Updated, adapted and several bug fixes on 2018 by Eduardo José Tagle
+ *
+ * This program is PUBLIC DOMAIN.
+ * This means that there is no copyright and anyone is able to take a copy
+ * for free and use it as they wish, with or without modifications, and in
+ * any context, commercially or otherwise. The only limitation is that I
+ * don't guarantee that the software is fit for any purpose or accept any
+ * liability for its use or misuse - this software is without warranty.
+ ***************************************************************************
+ * File Description: Abstract interpreter for ARM mode.
+ **************************************************************************/
+
+#if defined(__arm__) || defined(__thumb__)
+
+#define MODULE_NAME "UNWARM_ARM"
+
+#include
+#include "unwarm.h"
+
+/** Check if some instruction is a data-processing instruction.
+ * Decodes the passed instruction, checks if it is a data-processing and
+ * verifies that the parameters and operation really indicate a data-
+ * processing instruction. This is needed because some parts of the
+ * instruction space under this instruction can be extended or represent
+ * other operations such as MRS, MSR.
+ *
+ * \param[in] inst The instruction word.
+ * \retval true Further decoding of the instruction indicates that this is
+ * a valid data-processing instruction.
+ * \retval false This is not a data-processing instruction,
+ */
+static bool isDataProc(uint32_t instr) {
+ uint8_t opcode = (instr & 0x01E00000) >> 21;
+ if ((instr & 0xFC000000) != 0xE0000000) return false;
+
+ /* TST, TEQ, CMP and CMN all require S to be set */
+ bool S = !!(instr & 0x00100000);
+ if (!S && opcode >= 8 && opcode <= 11) return false;
+
+ return true;
+}
+
+UnwResult UnwStartArm(UnwState * const state) {
+ uint16_t t = UNW_MAX_INSTR_COUNT;
+
+ for (;;) {
+ uint32_t instr;
+
+ /* Attempt to read the instruction */
+ if (!state->cb->readW(state->regData[15].v, &instr))
+ return UNWIND_IREAD_W_FAIL;
+
+ UnwPrintd4("A %x %x %08x:", state->regData[13].v, state->regData[15].v, instr);
+
+ /* Check that the PC is still on Arm alignment */
+ if (state->regData[15].v & 0x3) {
+ UnwPrintd1("\nError: PC misalignment\n");
+ return UNWIND_INCONSISTENT;
+ }
+
+ /* Check that the SP and PC have not been invalidated */
+ if (!M_IsOriginValid(state->regData[13].o) || !M_IsOriginValid(state->regData[15].o)) {
+ UnwPrintd1("\nError: PC or SP invalidated\n");
+ return UNWIND_INCONSISTENT;
+ }
+
+ /* Branch and Exchange (BX)
+ * This is tested prior to data processing to prevent
+ * mis-interpretation as an invalid TEQ instruction.
+ */
+ if ((instr & 0xFFFFFFF0) == 0xE12FFF10) {
+ uint8_t rn = instr & 0xF;
+
+ UnwPrintd4("BX r%d\t ; r%d %s\n", rn, rn, M_Origin2Str(state->regData[rn].o));
+
+ if (!M_IsOriginValid(state->regData[rn].o)) {
+ UnwPrintd1("\nUnwind failure: BX to untracked register\n");
+ return UNWIND_FAILURE;
+ }
+
+ /* Set the new PC value */
+ state->regData[15].v = state->regData[rn].v;
+
+ /* Check if the return value is from the stack */
+ if (state->regData[rn].o == REG_VAL_FROM_STACK) {
+
+ /* Now have the return address */
+ UnwPrintd2(" Return PC=%x\n", state->regData[15].v & (~0x1));
+
+ /* Report the return address */
+ if (!UnwReportRetAddr(state, state->regData[rn].v))
+ return UNWIND_TRUNCATED;
+ }
+
+ /* Determine the return mode */
+ if (state->regData[rn].v & 0x1) /* Branching to THUMB */
+ return UnwStartThumb(state);
+
+ /* Branch to ARM */
+ /* Account for the auto-increment which isn't needed */
+ state->regData[15].v -= 4;
+ }
+ /* Branch */
+ else if ((instr & 0xFF000000) == 0xEA000000) {
+
+ int32_t offset = (instr & 0x00FFFFFF) << 2;
+
+ /* Sign extend if needed */
+ if (offset & 0x02000000) offset |= 0xFC000000;
+
+ UnwPrintd2("B %d\n", offset);
+
+ /* Adjust PC */
+ state->regData[15].v += offset;
+
+ /* Account for pre-fetch, where normally the PC is 8 bytes
+ * ahead of the instruction just executed.
+ */
+ state->regData[15].v += 4;
+ }
+
+ /* MRS */
+ else if ((instr & 0xFFBF0FFF) == 0xE10F0000) {
+ uint8_t rd = (instr & 0x0000F000) >> 12;
+ #ifdef UNW_DEBUG
+ const bool R = !!(instr & 0x00400000);
+ UnwPrintd4("MRS r%d,%s\t; r%d invalidated", rd, R ? "SPSR" : "CPSR", rd);
+ #endif
+
+ /* Status registers untracked */
+ state->regData[rd].o = REG_VAL_INVALID;
+ }
+ /* MSR */
+ else if ((instr & 0xFFB0F000) == 0xE120F000) {
+ #ifdef UNW_DEBUG
+ UnwPrintd2("MSR %s_?, ???", (instr & 0x00400000) ? "SPSR" : "CPSR");
+ #endif
+
+ /* Status registers untracked.
+ * Potentially this could change processor mode and switch
+ * banked registers r8-r14. Most likely is that r13 (sp) will
+ * be banked. However, invalidating r13 will stop unwinding
+ * when potentially this write is being used to disable/enable
+ * interrupts (a common case). Therefore no invalidation is
+ * performed.
+ */
+ }
+ /* Data processing */
+ else if (isDataProc(instr)) {
+ bool I = !!(instr & 0x02000000);
+ uint8_t opcode = (instr & 0x01E00000) >> 21;
+ #ifdef UNW_DEBUG
+ bool S = !!(instr & 0x00100000);
+ #endif
+ uint8_t rn = (instr & 0x000F0000) >> 16;
+ uint8_t rd = (instr & 0x0000F000) >> 12;
+ uint16_t operand2 = (instr & 0x00000FFF);
+ uint32_t op2val;
+ int op2origin;
+
+ switch (opcode) {
+ case 0: UnwPrintd4("AND%s r%d,r%d,", S ? "S" : "", rd, rn); break;
+ case 1: UnwPrintd4("EOR%s r%d,r%d,", S ? "S" : "", rd, rn); break;
+ case 2: UnwPrintd4("SUB%s r%d,r%d,", S ? "S" : "", rd, rn); break;
+ case 3: UnwPrintd4("RSB%s r%d,r%d,", S ? "S" : "", rd, rn); break;
+ case 4: UnwPrintd4("ADD%s r%d,r%d,", S ? "S" : "", rd, rn); break;
+ case 5: UnwPrintd4("ADC%s r%d,r%d,", S ? "S" : "", rd, rn); break;
+ case 6: UnwPrintd4("SBC%s r%d,r%d,", S ? "S" : "", rd, rn); break;
+ case 7: UnwPrintd4("RSC%s r%d,r%d,", S ? "S" : "", rd, rn); break;
+ case 8: UnwPrintd3("TST%s r%d,", S ? "S" : "", rn); break;
+ case 9: UnwPrintd3("TEQ%s r%d,", S ? "S" : "", rn); break;
+ case 10: UnwPrintd3("CMP%s r%d,", S ? "S" : "", rn); break;
+ case 11: UnwPrintd3("CMN%s r%d,", S ? "S" : "", rn); break;
+ case 12: UnwPrintd3("ORR%s r%d,", S ? "S" : "", rn); break;
+ case 13: UnwPrintd3("MOV%s r%d,", S ? "S" : "", rd); break;
+ case 14: UnwPrintd4("BIC%s r%d,r%d", S ? "S" : "", rd, rn); break;
+ case 15: UnwPrintd3("MVN%s r%d,", S ? "S" : "", rd); break;
+ }
+
+ /* Decode operand 2 */
+ if (I) {
+ uint8_t shiftDist = (operand2 & 0x0F00) >> 8;
+ uint8_t shiftConst = (operand2 & 0x00FF);
+
+ /* rotate const right by 2 * shiftDist */
+ shiftDist *= 2;
+ op2val = (shiftConst >> shiftDist) |
+ (shiftConst << (32 - shiftDist));
+ op2origin = REG_VAL_FROM_CONST;
+
+ UnwPrintd2("#0x%x", op2val);
+ }
+ else {
+
+ /* Register and shift */
+ uint8_t rm = (operand2 & 0x000F);
+ uint8_t regShift = !!(operand2 & 0x0010);
+ uint8_t shiftType = (operand2 & 0x0060) >> 5;
+ uint32_t shiftDist;
+ #ifdef UNW_DEBUG
+ const char * const shiftMnu[4] = { "LSL", "LSR", "ASR", "ROR" };
+ #endif
+ UnwPrintd2("r%d ", rm);
+
+ /* Get the shift distance */
+ if (regShift) {
+ uint8_t rs = (operand2 & 0x0F00) >> 8;
+
+ if (operand2 & 0x00800) {
+ UnwPrintd1("\nError: Bit should be zero\n");
+ return UNWIND_ILLEGAL_INSTR;
+ }
+ else if (rs == 15) {
+ UnwPrintd1("\nError: Cannot use R15 with register shift\n");
+ return UNWIND_ILLEGAL_INSTR;
+ }
+
+ /* Get shift distance */
+ shiftDist = state->regData[rs].v;
+ op2origin = state->regData[rs].o;
+
+ UnwPrintd7("%s r%d\t; r%d %s r%d %s", shiftMnu[shiftType], rs, rm, M_Origin2Str(state->regData[rm].o), rs, M_Origin2Str(state->regData[rs].o));
+ }
+ else {
+ shiftDist = (operand2 & 0x0F80) >> 7;
+ op2origin = REG_VAL_FROM_CONST;
+ if (shiftDist) UnwPrintd3("%s #%d", shiftMnu[shiftType], shiftDist);
+ UnwPrintd3("\t; r%d %s", rm, M_Origin2Str(state->regData[rm].o));
+ }
+
+ /* Apply the shift type to the source register */
+ switch (shiftType) {
+ case 0: /* logical left */
+ op2val = state->regData[rm].v << shiftDist;
+ break;
+
+ case 1: /* logical right */
+ if (!regShift && shiftDist == 0) shiftDist = 32;
+ op2val = state->regData[rm].v >> shiftDist;
+ break;
+
+ case 2: /* arithmetic right */
+ if (!regShift && shiftDist == 0) shiftDist = 32;
+
+ if (state->regData[rm].v & 0x80000000) {
+ /* Register shifts maybe greater than 32 */
+ if (shiftDist >= 32)
+ op2val = 0xFFFFFFFF;
+ else
+ op2val = (state->regData[rm].v >> shiftDist) | (0xFFFFFFFF << (32 - shiftDist));
+ }
+ else
+ op2val = state->regData[rm].v >> shiftDist;
+ break;
+
+ case 3: /* rotate right */
+
+ if (!regShift && shiftDist == 0) {
+ /* Rotate right with extend.
+ * This uses the carry bit and so always has an
+ * untracked result.
+ */
+ op2origin = REG_VAL_INVALID;
+ op2val = 0;
+ }
+ else {
+ /* Limit shift distance to 0-31 incase of register shift */
+ shiftDist &= 0x1F;
+
+ op2val = (state->regData[rm].v >> shiftDist) |
+ (state->regData[rm].v << (32 - shiftDist));
+ }
+ break;
+
+ default:
+ UnwPrintd2("\nError: Invalid shift type: %d\n", shiftType);
+ return UNWIND_FAILURE;
+ }
+
+ /* Decide the data origin */
+ if (M_IsOriginValid(op2origin) && M_IsOriginValid(state->regData[rm].o))
+ op2origin = REG_VAL_ARITHMETIC | state->regData[rm].o;
+ else
+ op2origin = REG_VAL_INVALID;
+ }
+
+ /* Propagate register validity */
+ switch (opcode) {
+ case 0: /* AND: Rd := Op1 AND Op2 */
+ case 1: /* EOR: Rd := Op1 EOR Op2 */
+ case 2: /* SUB: Rd:= Op1 - Op2 */
+ case 3: /* RSB: Rd:= Op2 - Op1 */
+ case 4: /* ADD: Rd:= Op1 + Op2 */
+ case 12: /* ORR: Rd:= Op1 OR Op2 */
+ case 14: /* BIC: Rd:= Op1 AND NOT Op2 */
+ if (!M_IsOriginValid(state->regData[rn].o) ||
+ !M_IsOriginValid(op2origin)) {
+ state->regData[rd].o = REG_VAL_INVALID;
+ }
+ else {
+ state->regData[rd].o = state->regData[rn].o;
+ state->regData[rd].o = (RegValOrigin)(state->regData[rd].o | op2origin);
+ }
+ break;
+
+ case 5: /* ADC: Rd:= Op1 + Op2 + C */
+ case 6: /* SBC: Rd:= Op1 - Op2 + C */
+ case 7: /* RSC: Rd:= Op2 - Op1 + C */
+ /* CPSR is not tracked */
+ state->regData[rd].o = REG_VAL_INVALID;
+ break;
+
+ case 8: /* TST: set condition codes on Op1 AND Op2 */
+ case 9: /* TEQ: set condition codes on Op1 EOR Op2 */
+ case 10: /* CMP: set condition codes on Op1 - Op2 */
+ case 11: /* CMN: set condition codes on Op1 + Op2 */
+ break;
+
+ case 13: /* MOV: Rd:= Op2 */
+ case 15: /* MVN: Rd:= NOT Op2 */
+ state->regData[rd].o = (RegValOrigin) op2origin;
+ break;
+ }
+
+ /* Account for pre-fetch by temporarily adjusting PC */
+ if (rn == 15) {
+
+ /* If the shift amount is specified in the instruction,
+ * the PC will be 8 bytes ahead. If a register is used
+ * to specify the shift amount the PC will be 12 bytes
+ * ahead.
+ */
+ state->regData[rn].v += ((!I && (operand2 & 0x0010)) ? 12 : 8);
+ }
+
+ /* Compute values */
+ switch (opcode) {
+ case 0: /* AND: Rd := Op1 AND Op2 */
+ state->regData[rd].v = state->regData[rn].v & op2val;
+ break;
+
+ case 1: /* EOR: Rd := Op1 EOR Op2 */
+ state->regData[rd].v = state->regData[rn].v ^ op2val;
+ break;
+
+ case 2: /* SUB: Rd:= Op1 - Op2 */
+ state->regData[rd].v = state->regData[rn].v - op2val;
+ break;
+ case 3: /* RSB: Rd:= Op2 - Op1 */
+ state->regData[rd].v = op2val - state->regData[rn].v;
+ break;
+
+ case 4: /* ADD: Rd:= Op1 + Op2 */
+ state->regData[rd].v = state->regData[rn].v + op2val;
+ break;
+
+ case 5: /* ADC: Rd:= Op1 + Op2 + C */
+ case 6: /* SBC: Rd:= Op1 - Op2 + C */
+ case 7: /* RSC: Rd:= Op2 - Op1 + C */
+ case 8: /* TST: set condition codes on Op1 AND Op2 */
+ case 9: /* TEQ: set condition codes on Op1 EOR Op2 */
+ case 10: /* CMP: set condition codes on Op1 - Op2 */
+ case 11: /* CMN: set condition codes on Op1 + Op2 */
+ UnwPrintd1("\t; ????");
+ break;
+
+ case 12: /* ORR: Rd:= Op1 OR Op2 */
+ state->regData[rd].v = state->regData[rn].v | op2val;
+ break;
+
+ case 13: /* MOV: Rd:= Op2 */
+ state->regData[rd].v = op2val;
+ break;
+
+ case 14: /* BIC: Rd:= Op1 AND NOT Op2 */
+ state->regData[rd].v = state->regData[rn].v & (~op2val);
+ break;
+
+ case 15: /* MVN: Rd:= NOT Op2 */
+ state->regData[rd].v = ~op2val;
+ break;
+ }
+
+ /* Remove the prefetch offset from the PC */
+ if (rd != 15 && rn == 15)
+ state->regData[rn].v -= ((!I && (operand2 & 0x0010)) ? 12 : 8);
+ }
+
+ /* Block Data Transfer
+ * LDM, STM
+ */
+ else if ((instr & 0xFE000000) == 0xE8000000) {
+
+ bool P = !!(instr & 0x01000000),
+ U = !!(instr & 0x00800000),
+ S = !!(instr & 0x00400000),
+ W = !!(instr & 0x00200000),
+ L = !!(instr & 0x00100000);
+ uint16_t baseReg = (instr & 0x000F0000) >> 16;
+ uint16_t regList = (instr & 0x0000FFFF);
+ uint32_t addr = state->regData[baseReg].v;
+ bool addrValid = M_IsOriginValid(state->regData[baseReg].o);
+ int8_t r;
+
+ #ifdef UNW_DEBUG
+ /* Display the instruction */
+ if (L)
+ UnwPrintd6("LDM%c%c r%d%s, {reglist}%s\n", P ? 'E' : 'F', U ? 'D' : 'A', baseReg, W ? "!" : "", S ? "^" : "");
+ else
+ UnwPrintd6("STM%c%c r%d%s, {reglist}%s\n", !P ? 'E' : 'F', !U ? 'D' : 'A', baseReg, W ? "!" : "", S ? "^" : "");
+ #endif
+
+ /* S indicates that banked registers (untracked) are used, unless
+ * this is a load including the PC when the S-bit indicates that
+ * that CPSR is loaded from SPSR (also untracked, but ignored).
+ */
+ if (S && (!L || (regList & (0x01 << 15)) == 0)) {
+ UnwPrintd1("\nError:S-bit set requiring banked registers\n");
+ return UNWIND_FAILURE;
+ }
+ else if (baseReg == 15) {
+ UnwPrintd1("\nError: r15 used as base register\n");
+ return UNWIND_FAILURE;
+ }
+ else if (regList == 0) {
+ UnwPrintd1("\nError: Register list empty\n");
+ return UNWIND_FAILURE;
+ }
+
+ /* Check if ascending or descending.
+ * Registers are loaded/stored in order of address.
+ * i.e. r0 is at the lowest address, r15 at the highest.
+ */
+ r = U ? 0 : 15;
+ do {
+
+ /* Check if the register is to be transferred */
+ if (regList & (0x01 << r)) {
+
+ if (P) addr += U ? 4 : -4;
+
+ if (L) {
+
+ if (addrValid) {
+
+ if (!UnwMemReadRegister(state, addr, &state->regData[r]))
+ return UNWIND_DREAD_W_FAIL;
+
+ /* Update the origin if read via the stack pointer */
+ if (M_IsOriginValid(state->regData[r].o) && baseReg == 13)
+ state->regData[r].o = REG_VAL_FROM_STACK;
+
+ UnwPrintd5(" R%d = 0x%08x\t; r%d %s\n",r,state->regData[r].v,r, M_Origin2Str(state->regData[r].o));
+ }
+ else {
+ /* Invalidate the register as the base reg was invalid */
+ state->regData[r].o = REG_VAL_INVALID;
+ UnwPrintd2(" R%d = ???\n", r);
+ }
+ }
+ else {
+ if (addrValid && !UnwMemWriteRegister(state, state->regData[13].v, &state->regData[r]))
+ return UNWIND_DWRITE_W_FAIL;
+
+ UnwPrintd2(" R%d = 0x%08x\n", r);
+ }
+
+ if (!P) addr += U ? 4 : -4;
+ }
+
+ /* Check the next register */
+ r += U ? 1 : -1;
+
+ } while (r >= 0 && r <= 15);
+
+ /* Check the writeback bit */
+ if (W) state->regData[baseReg].v = addr;
+
+ /* Check if the PC was loaded */
+ if (L && (regList & (0x01 << 15))) {
+ if (!M_IsOriginValid(state->regData[15].o)) {
+ /* Return address is not valid */
+ UnwPrintd1("PC popped with invalid address\n");
+ return UNWIND_FAILURE;
+ }
+ else {
+ /* Store the return address */
+ if (!UnwReportRetAddr(state, state->regData[15].v))
+ return UNWIND_TRUNCATED;
+
+ UnwPrintd2(" Return PC=0x%x", state->regData[15].v);
+
+ /* Determine the return mode */
+ if (state->regData[15].v & 0x1) {
+ /* Branching to THUMB */
+ return UnwStartThumb(state);
+ }
+ else {
+ /* Branch to ARM */
+
+ /* Account for the auto-increment which isn't needed */
+ state->regData[15].v -= 4;
+ }
+ }
+ }
+ }
+ else {
+ UnwPrintd1("????");
+
+ /* Unknown/undecoded. May alter some register, so invalidate file */
+ UnwInvalidateRegisterFile(state->regData);
+ }
+
+ UnwPrintd1("\n");
+
+ /* Should never hit the reset vector */
+ if (state->regData[15].v == 0) return UNWIND_RESET;
+
+ /* Check next address */
+ state->regData[15].v += 4;
+
+ /* Garbage collect the memory hash (used only for the stack) */
+ UnwMemHashGC(state);
+
+ if (--t == 0) return UNWIND_EXHAUSTED;
+
+ }
+
+ return UNWIND_UNSUPPORTED;
+}
+
+#endif // __arm__ || __thumb__
diff --git a/Marlin/src/HAL/shared/backtrace/unwarm_thumb.cpp b/Marlin/src/HAL/shared/backtrace/unwarm_thumb.cpp
new file mode 100644
index 0000000000..0c6a70649d
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/unwarm_thumb.cpp
@@ -0,0 +1,1066 @@
+/***************************************************************************
+ * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
+ * Updated, adapted and several bug fixes on 2018 by Eduardo José Tagle
+ *
+ * This program is PUBLIC DOMAIN.
+ * This means that there is no copyright and anyone is able to take a copy
+ * for free and use it as they wish, with or without modifications, and in
+ * any context, commercially or otherwise. The only limitation is that I
+ * don't guarantee that the software is fit for any purpose or accept any
+ * liability for its use or misuse - this software is without warranty.
+ ***************************************************************************
+ * File Description: Abstract interpretation for Thumb mode.
+ **************************************************************************/
+
+#if defined(__arm__) || defined(__thumb__)
+
+#define MODULE_NAME "UNWARM_THUMB"
+
+#include
+#include "unwarm.h"
+
+/** Sign extend an 11 bit value.
+ * This function simply inspects bit 11 of the input \a value, and if
+ * set, the top 5 bits are set to give a 2's compliment signed value.
+ * \param value The value to sign extend.
+ * \return The signed-11 bit value stored in a 16bit data type.
+ */
+static int32_t signExtend11(const uint16_t value) {
+ return (value & 0x400) ? value | 0xFFFFF800 : value;
+}
+
+UnwResult UnwStartThumb(UnwState * const state) {
+ uint16_t t = UNW_MAX_INSTR_COUNT;
+ uint32_t lastJumpAddr = 0; // Last JUMP address, to try to detect infinite loops
+ bool loopDetected = false; // If a loop was detected
+
+ for (;;) {
+ uint16_t instr;
+
+ /* Attempt to read the instruction */
+ if (!state->cb->readH(state->regData[15].v & (~0x1), &instr))
+ return UNWIND_IREAD_H_FAIL;
+
+ UnwPrintd4("T %x %x %04x:", state->regData[13].v, state->regData[15].v, instr);
+
+ /* Check that the PC is still on Thumb alignment */
+ if (!(state->regData[15].v & 0x1)) {
+ UnwPrintd1("\nError: PC misalignment\n");
+ return UNWIND_INCONSISTENT;
+ }
+
+ /* Check that the SP and PC have not been invalidated */
+ if (!M_IsOriginValid(state->regData[13].o) || !M_IsOriginValid(state->regData[15].o)) {
+ UnwPrintd1("\nError: PC or SP invalidated\n");
+ return UNWIND_INCONSISTENT;
+ }
+
+ /*
+ * Detect 32bit thumb instructions
+ */
+ if ((instr & 0xE000) == 0xE000 && (instr & 0x1800) != 0) {
+ uint16_t instr2;
+
+ /* Check next address */
+ state->regData[15].v += 2;
+
+ /* Attempt to read the 2nd part of the instruction */
+ if (!state->cb->readH(state->regData[15].v & (~0x1), &instr2))
+ return UNWIND_IREAD_H_FAIL;
+
+ UnwPrintd3(" %x %04x:", state->regData[15].v, instr2);
+
+ /*
+ * Load/Store multiple: Only interpret
+ * PUSH and POP
+ */
+ if ((instr & 0xFE6F) == 0xE82D) {
+ bool L = !!(instr & 0x10);
+ uint16_t rList = instr2;
+
+ if (L) {
+ uint8_t r;
+
+ /* Load from memory: POP */
+ UnwPrintd1("POP {Rlist}\n");
+
+ /* Load registers from stack */
+ for (r = 0; r < 16; r++) {
+ if (rList & (0x1 << r)) {
+
+ /* Read the word */
+ if (!UnwMemReadRegister(state, state->regData[13].v, &state->regData[r]))
+ return UNWIND_DREAD_W_FAIL;
+
+ /* Alter the origin to be from the stack if it was valid */
+ if (M_IsOriginValid(state->regData[r].o)) {
+
+ state->regData[r].o = REG_VAL_FROM_STACK;
+
+ /* If restoring the PC */
+ if (r == 15) {
+
+ /* The bottom bit should have been set to indicate that
+ * the caller was from Thumb. This would allow return
+ * by BX for interworking APCS.
+ */
+ if ((state->regData[15].v & 0x1) == 0) {
+ UnwPrintd2("Warning: Return address not to Thumb: 0x%08x\n", state->regData[15].v);
+
+ /* Pop into the PC will not switch mode */
+ return UNWIND_INCONSISTENT;
+ }
+
+ /* Store the return address */
+ if (!UnwReportRetAddr(state, state->regData[15].v))
+ return UNWIND_TRUNCATED;
+
+ /* Now have the return address */
+ UnwPrintd2(" Return PC=%x\n", state->regData[15].v);
+
+ /* Compensate for the auto-increment, which isn't needed here */
+ state->regData[15].v -= 2;
+
+ }
+
+ } else {
+
+ if (r == 15) {
+ /* Return address is not valid */
+ UnwPrintd1("PC popped with invalid address\n");
+ return UNWIND_FAILURE;
+ }
+ }
+
+ state->regData[13].v += 4;
+
+ UnwPrintd3(" r%d = 0x%08x\n", r, state->regData[r].v);
+ }
+ }
+ }
+ else {
+ int8_t r;
+
+ /* Store to memory: PUSH */
+ UnwPrintd1("PUSH {Rlist}");
+
+ for (r = 15; r >= 0; r--) {
+ if (rList & (0x1 << r)) {
+ UnwPrintd4("\n r%d = 0x%08x\t; %s", r, state->regData[r].v, M_Origin2Str(state->regData[r].o));
+
+ state->regData[13].v -= 4;
+
+ if (!UnwMemWriteRegister(state, state->regData[13].v, &state->regData[r]))
+ return UNWIND_DWRITE_W_FAIL;
+ }
+ }
+ }
+ }
+ /*
+ * PUSH register
+ */
+ else if (instr == 0xF84D && (instr2 & 0x0FFF) == 0x0D04) {
+ uint8_t r = instr2 >> 12;
+
+ /* Store to memory: PUSH */
+ UnwPrintd2("PUSH {R%d}\n", r);
+ UnwPrintd4("\n r%d = 0x%08x\t; %s", r, state->regData[r].v, M_Origin2Str(state->regData[r].o));
+
+ state->regData[13].v -= 4;
+
+ if (!UnwMemWriteRegister(state, state->regData[13].v, &state->regData[r]))
+ return UNWIND_DWRITE_W_FAIL;
+ }
+ /*
+ * POP register
+ */
+ else if (instr == 0xF85D && (instr2 & 0x0FFF) == 0x0B04) {
+ uint8_t r = instr2 >> 12;
+
+ /* Load from memory: POP */
+ UnwPrintd2("POP {R%d}\n", r);
+
+ /* Read the word */
+ if (!UnwMemReadRegister(state, state->regData[13].v, &state->regData[r]))
+ return UNWIND_DREAD_W_FAIL;
+
+ /* Alter the origin to be from the stack if it was valid */
+ if (M_IsOriginValid(state->regData[r].o)) {
+
+ state->regData[r].o = REG_VAL_FROM_STACK;
+
+ /* If restoring the PC */
+ if (r == 15) {
+
+ /* The bottom bit should have been set to indicate that
+ * the caller was from Thumb. This would allow return
+ * by BX for interworking APCS.
+ */
+ if ((state->regData[15].v & 0x1) == 0) {
+ UnwPrintd2("Warning: Return address not to Thumb: 0x%08x\n", state->regData[15].v);
+
+ /* Pop into the PC will not switch mode */
+ return UNWIND_INCONSISTENT;
+ }
+
+ /* Store the return address */
+ if (!UnwReportRetAddr(state, state->regData[15].v))
+ return UNWIND_TRUNCATED;
+
+ /* Now have the return address */
+ UnwPrintd2(" Return PC=%x\n", state->regData[15].v);
+
+ /* Compensate for the auto-increment, which isn't needed here */
+ state->regData[15].v -= 2;
+
+ }
+
+ } else {
+
+ if (r == 15) {
+ /* Return address is not valid */
+ UnwPrintd1("PC popped with invalid address\n");
+ return UNWIND_FAILURE;
+ }
+ }
+
+ state->regData[13].v += 4;
+
+ UnwPrintd3(" r%d = 0x%08x\n", r, state->regData[r].v);
+ }
+ /*
+ * TBB / TBH
+ */
+ else if ((instr & 0xFFF0) == 0xE8D0 && (instr2 & 0xFFE0) == 0xF000) {
+ /* We are only interested in
+ * the forms
+ * TBB [PC, ...]
+ * TBH [PC, ..., LSL #1]
+ * as those are used by the C compiler to implement
+ * the switch clauses
+ */
+ uint8_t rn = instr & 0xF;
+ bool H = !!(instr2 & 0x10);
+
+ UnwPrintd5("TB%c [r%d,r%d%s]\n", H ? 'H' : 'B', rn, (instr2 & 0xF), H ? ",LSL #1" : "");
+
+ // We are only interested if the RN is the PC. Let's choose the 1st destination
+ if (rn == 15) {
+ if (H) {
+ uint16_t rv;
+ if (!state->cb->readH((state->regData[15].v & (~1)) + 2, &rv))
+ return UNWIND_DREAD_H_FAIL;
+ state->regData[15].v += rv * 2;
+ }
+ else {
+ uint8_t rv;
+ if (!state->cb->readB((state->regData[15].v & (~1)) + 2, &rv))
+ return UNWIND_DREAD_B_FAIL;
+ state->regData[15].v += rv * 2;
+ }
+ }
+ }
+ /*
+ * Unconditional branch
+ */
+ else if ((instr & 0xF800) == 0xF000 && (instr2 & 0xD000) == 0x9000) {
+ uint32_t v;
+
+ uint8_t S = (instr & 0x400) >> 10;
+ uint16_t imm10 = (instr & 0x3FF);
+ uint8_t J1 = (instr2 & 0x2000) >> 13;
+ uint8_t J2 = (instr2 & 0x0800) >> 11;
+ uint16_t imm11 = (instr2 & 0x7FF);
+
+ uint8_t I1 = J1 ^ S ^ 1;
+ uint8_t I2 = J2 ^ S ^ 1;
+ uint32_t imm32 = (S << 24) | (I1 << 23) | (I2 << 22) |(imm10 << 12) | (imm11 << 1);
+ if (S) imm32 |= 0xFE000000;
+
+ UnwPrintd2("B %d \n", imm32);
+
+ /* Update PC */
+ state->regData[15].v += imm32;
+
+ /* Need to advance by a word to account for pre-fetch.
+ * Advance by a half word here, allowing the normal address
+ * advance to account for the other half word.
+ */
+ state->regData[15].v += 2;
+
+ /* Compute the jump address */
+ v = state->regData[15].v + 2;
+
+ /* Display PC of next instruction */
+ UnwPrintd2(" New PC=%x", v);
+
+ /* Did we detect an infinite loop ? */
+ loopDetected = lastJumpAddr == v;
+
+ /* Remember the last address we jumped to */
+ lastJumpAddr = v;
+ }
+
+ /*
+ * Branch with link
+ */
+ else if ((instr & 0xF800) == 0xF000 && (instr2 & 0xD000) == 0xD000) {
+
+ uint8_t S = (instr & 0x400) >> 10;
+ uint16_t imm10 = (instr & 0x3FF);
+ uint8_t J1 = (instr2 & 0x2000) >> 13;
+ uint8_t J2 = (instr2 & 0x0800) >> 11;
+ uint16_t imm11 = (instr2 & 0x7FF);
+
+ uint8_t I1 = J1 ^ S ^ 1;
+ uint8_t I2 = J2 ^ S ^ 1;
+ uint32_t imm32 = (S << 24) | (I1 << 23) | (I2 << 22) |(imm10 << 12) | (imm11 << 1);
+ if (S) imm32 |= 0xFE000000;
+
+ UnwPrintd2("BL %d \n", imm32);
+
+ /* Never taken, as we are unwinding the stack */
+ if (0) {
+
+ /* Store return address in LR register */
+ state->regData[14].v = state->regData[15].v + 2;
+ state->regData[14].o = REG_VAL_FROM_CONST;
+
+ /* Update PC */
+ state->regData[15].v += imm32;
+
+ /* Need to advance by a word to account for pre-fetch.
+ * Advance by a half word here, allowing the normal address
+ * advance to account for the other half word.
+ */
+ state->regData[15].v += 2;
+
+ /* Display PC of next instruction */
+ UnwPrintd2(" Return PC=%x", state->regData[15].v);
+
+ /* Report the return address, including mode bit */
+ if (!UnwReportRetAddr(state, state->regData[15].v))
+ return UNWIND_TRUNCATED;
+
+ /* Determine the new mode */
+ if (state->regData[15].v & 0x1) {
+ /* Branching to THUMB */
+
+ /* Account for the auto-increment which isn't needed */
+ state->regData[15].v -= 2;
+ }
+ else {
+ /* Branch to ARM */
+ return UnwStartArm(state);
+ }
+ }
+ }
+
+ /*
+ * Conditional branches. Usually not taken, unless infinite loop is detected
+ */
+ else if ((instr & 0xF800) == 0xF000 && (instr2 & 0xD000) == 0x8000) {
+
+ uint8_t S = (instr & 0x400) >> 10;
+ uint16_t imm6 = (instr & 0x3F);
+ uint8_t J1 = (instr2 & 0x2000) >> 13;
+ uint8_t J2 = (instr2 & 0x0800) >> 11;
+ uint16_t imm11 = (instr2 & 0x7FF);
+
+ uint8_t I1 = J1 ^ S ^ 1;
+ uint8_t I2 = J2 ^ S ^ 1;
+ uint32_t imm32 = (S << 20) | (I1 << 19) | (I2 << 18) |(imm6 << 12) | (imm11 << 1);
+ if (S) imm32 |= 0xFFE00000;
+
+ UnwPrintd2("Bcond %d\n", imm32);
+
+ /* Take the jump only if a loop is detected */
+ if (loopDetected) {
+
+ /* Update PC */
+ state->regData[15].v += imm32;
+
+ /* Need to advance by a word to account for pre-fetch.
+ * Advance by a half word here, allowing the normal address
+ * advance to account for the other half word.
+ */
+ state->regData[15].v += 2;
+
+ /* Display PC of next instruction */
+ UnwPrintd2(" New PC=%x", state->regData[15].v + 2);
+ }
+ }
+ /*
+ * PC-relative load
+ * LDR Rd,[PC, #+/-imm]
+ */
+ else if ((instr & 0xFF7F) == 0xF85F) {
+ uint8_t rt = (instr2 & 0xF000) >> 12;
+ uint8_t imm12 = (instr2 & 0x0FFF);
+ bool A = !!(instr & 0x80);
+ uint32_t address;
+
+ /* Compute load address, adding a word to account for prefetch */
+ address = (state->regData[15].v & (~0x3)) + 4;
+ if (A) address += imm12;
+ else address -= imm12;
+
+ UnwPrintd4("LDR r%d,[PC #%c0x%08x]", rt, A?'+':'-', address);
+
+ if (!UnwMemReadRegister(state, address, &state->regData[rt]))
+ return UNWIND_DREAD_W_FAIL;
+ }
+ /*
+ * LDR immediate.
+ * We are only interested when destination is PC.
+ * LDR Rt,[Rn , #n]
+ */
+ else if ((instr & 0xFFF0) == 0xF8D0) {
+ uint8_t rn = (instr & 0xF);
+ uint8_t rt = (instr2 & 0xF000) >> 12;
+ uint16_t imm12 = (instr2 & 0xFFF);
+
+ /* If destination is PC and we don't know the source value, then fail */
+ if (!M_IsOriginValid(state->regData[rn].o)) {
+ state->regData[rt].o = state->regData[rn].o;
+ }
+ else {
+ uint32_t address = state->regData[rn].v + imm12;
+ if (!UnwMemReadRegister(state, address, &state->regData[rt]))
+ return UNWIND_DREAD_W_FAIL;
+ }
+ }
+ /*
+ * LDR immediate
+ * We are only interested when destination is PC.
+ * LDR Rt,[Rn , #-n]
+ * LDR Rt,[Rn], #+/-n]
+ * LDR Rt,[Rn, #+/-n]!
+ */
+ else if ((instr & 0xFFF0) == 0xF850 && (instr2 & 0x0800) == 0x0800) {
+ uint8_t rn = (instr & 0xF);
+ uint8_t rt = (instr2 & 0xF000) >> 12;
+ uint16_t imm8 = (instr2 & 0xFF);
+ bool P = !!(instr2 & 0x400);
+ bool U = !!(instr2 & 0x200);
+ bool W = !!(instr2 & 0x100);
+
+ if (!M_IsOriginValid(state->regData[rn].o))
+ state->regData[rt].o = state->regData[rn].o;
+ else {
+ uint32_t offaddress = state->regData[rn].v + (U ? imm8 + imm8 : 0),
+ address = P ? offaddress : state->regData[rn].v;
+
+ if (!UnwMemReadRegister(state, address, &state->regData[rt]))
+ return UNWIND_DREAD_W_FAIL;
+
+ if (W) state->regData[rn].v = offaddress;
+ }
+ }
+ /*
+ * LDR (register).
+ * We are interested in the form
+ * ldr Rt, [Rn, Rm, lsl #x]
+ * Where Rt is PC, Rn value is known, Rm is not known or unknown
+ */
+ else if ((instr & 0xFFF0) == 0xF850 && (instr2 & 0x0FC0) == 0x0000) {
+ const uint8_t rn = (instr & 0xF),
+ rt = (instr2 & 0xF000) >> 12,
+ rm = (instr2 & 0xF),
+ imm2 = (instr2 & 0x30) >> 4;
+
+ if (!M_IsOriginValid(state->regData[rn].o) || !M_IsOriginValid(state->regData[rm].o)) {
+
+ /* If Rt is PC, and Rn is known, then do an exception and assume
+ Rm equals 0 => This takes the first case in a switch() */
+ if (rt == 15 && M_IsOriginValid(state->regData[rn].o)) {
+ uint32_t address = state->regData[rn].v;
+ if (!UnwMemReadRegister(state, address, &state->regData[rt]))
+ return UNWIND_DREAD_W_FAIL;
+ }
+ else /* Propagate unknown value */
+ state->regData[rt].o = state->regData[rn].o;
+
+ }
+ else {
+ uint32_t address = state->regData[rn].v + (state->regData[rm].v << imm2);
+ if (!UnwMemReadRegister(state, address, &state->regData[rt]))
+ return UNWIND_DREAD_W_FAIL;
+ }
+ }
+ else {
+ UnwPrintd1("???? (32)");
+
+ /* Unknown/undecoded. May alter some register, so invalidate file */
+ UnwInvalidateRegisterFile(state->regData);
+ }
+ /* End of thumb 32bit code */
+
+ }
+ /* Format 1: Move shifted register
+ * LSL Rd, Rs, #Offset5
+ * LSR Rd, Rs, #Offset5
+ * ASR Rd, Rs, #Offset5
+ */
+ else if ((instr & 0xE000) == 0x0000 && (instr & 0x1800) != 0x1800) {
+ bool signExtend;
+ const uint8_t op = (instr & 0x1800) >> 11,
+ offset5 = (instr & 0x07C0) >> 6,
+ rs = (instr & 0x0038) >> 3,
+ rd = (instr & 0x0007);
+
+ switch (op) {
+ case 0: /* LSL */
+ UnwPrintd6("LSL r%d, r%d, #%d\t; r%d %s", rd, rs, offset5, rs, M_Origin2Str(state->regData[rs].o));
+ state->regData[rd].v = state->regData[rs].v << offset5;
+ state->regData[rd].o = state->regData[rs].o;
+ state->regData[rd].o |= REG_VAL_ARITHMETIC;
+ break;
+
+ case 1: /* LSR */
+ UnwPrintd6("LSR r%d, r%d, #%d\t; r%d %s", rd, rs, offset5, rs, M_Origin2Str(state->regData[rs].o));
+ state->regData[rd].v = state->regData[rs].v >> offset5;
+ state->regData[rd].o = state->regData[rs].o;
+ state->regData[rd].o |= REG_VAL_ARITHMETIC;
+ break;
+
+ case 2: /* ASR */
+ UnwPrintd6("ASL r%d, r%d, #%d\t; r%d %s", rd, rs, offset5, rs, M_Origin2Str(state->regData[rs].o));
+
+ signExtend = !!(state->regData[rs].v & 0x8000);
+ state->regData[rd].v = state->regData[rs].v >> offset5;
+ if (signExtend) state->regData[rd].v |= 0xFFFFFFFF << (32 - offset5);
+ state->regData[rd].o = state->regData[rs].o;
+ state->regData[rd].o |= REG_VAL_ARITHMETIC;
+ break;
+ }
+ }
+ /* Format 2: add/subtract
+ * ADD Rd, Rs, Rn
+ * ADD Rd, Rs, #Offset3
+ * SUB Rd, Rs, Rn
+ * SUB Rd, Rs, #Offset3
+ */
+ else if ((instr & 0xF800) == 0x1800) {
+ bool I = !!(instr & 0x0400);
+ bool op = !!(instr & 0x0200);
+ uint8_t rn = (instr & 0x01C0) >> 6;
+ uint8_t rs = (instr & 0x0038) >> 3;
+ uint8_t rd = (instr & 0x0007);
+
+ /* Print decoding */
+ UnwPrintd6("%s r%d, r%d, %c%d\t;",op ? "SUB" : "ADD",rd, rs,I ? '#' : 'r',rn);
+ UnwPrintd5("r%d %s, r%d %s",rd, M_Origin2Str(state->regData[rd].o),rs, M_Origin2Str(state->regData[rs].o));
+ if (!I) {
+
+ UnwPrintd3(", r%d %s", rn, M_Origin2Str(state->regData[rn].o));
+
+ /* Perform calculation */
+ state->regData[rd].v = state->regData[rs].v + (op ? -state->regData[rn].v : state->regData[rn].v);
+
+ /* Propagate the origin */
+ if (M_IsOriginValid(state->regData[rs].o) && M_IsOriginValid(state->regData[rn].o)) {
+ state->regData[rd].o = state->regData[rs].o;
+ state->regData[rd].o |= REG_VAL_ARITHMETIC;
+ }
+ else
+ state->regData[rd].o = REG_VAL_INVALID;
+ }
+ else {
+ /* Perform calculation */
+ state->regData[rd].v = state->regData[rs].v + (op ? -rn : rn);
+
+ /* Propagate the origin */
+ state->regData[rd].o = state->regData[rs].o;
+ state->regData[rd].o |= REG_VAL_ARITHMETIC;
+ }
+ }
+ /* Format 3: move/compare/add/subtract immediate
+ * MOV Rd, #Offset8
+ * CMP Rd, #Offset8
+ * ADD Rd, #Offset8
+ * SUB Rd, #Offset8
+ */
+ else if ((instr & 0xE000) == 0x2000) {
+
+ uint8_t op = (instr & 0x1800) >> 11;
+ uint8_t rd = (instr & 0x0700) >> 8;
+ uint8_t offset8 = (instr & 0x00FF);
+
+ switch (op) {
+ case 0: /* MOV */
+ UnwPrintd3("MOV r%d, #0x%x", rd, offset8);
+ state->regData[rd].v = offset8;
+ state->regData[rd].o = REG_VAL_FROM_CONST;
+ break;
+
+ case 1: /* CMP */
+ /* Irrelevant to unwinding */
+ UnwPrintd1("CMP ???");
+ break;
+
+ case 2: /* ADD */
+ UnwPrintd5("ADD r%d, #0x%x\t; r%d %s", rd, offset8, rd, M_Origin2Str(state->regData[rd].o));
+ state->regData[rd].v += offset8;
+ state->regData[rd].o |= REG_VAL_ARITHMETIC;
+ break;
+
+ case 3: /* SUB */
+ UnwPrintd5("SUB r%d, #0x%d\t; r%d %s", rd, offset8, rd, M_Origin2Str(state->regData[rd].o));
+ state->regData[rd].v -= offset8;
+ state->regData[rd].o |= REG_VAL_ARITHMETIC;
+ break;
+ }
+ }
+ /* Format 4: ALU operations
+ * AND Rd, Rs
+ * EOR Rd, Rs
+ * LSL Rd, Rs
+ * LSR Rd, Rs
+ * ASR Rd, Rs
+ * ADC Rd, Rs
+ * SBC Rd, Rs
+ * ROR Rd, Rs
+ * TST Rd, Rs
+ * NEG Rd, Rs
+ * CMP Rd, Rs
+ * CMN Rd, Rs
+ * ORR Rd, Rs
+ * MUL Rd, Rs
+ * BIC Rd, Rs
+ * MVN Rd, Rs
+ */
+ else if ((instr & 0xFC00) == 0x4000) {
+ uint8_t op = (instr & 0x03C0) >> 6;
+ uint8_t rs = (instr & 0x0038) >> 3;
+ uint8_t rd = (instr & 0x0007);
+
+#ifdef UNW_DEBUG
+ static const char * const mnu[16] = {
+ "AND", "EOR", "LSL", "LSR",
+ "ASR", "ADC", "SBC", "ROR",
+ "TST", "NEG", "CMP", "CMN",
+ "ORR", "MUL", "BIC", "MVN" };
+#endif
+ /* Print the mnemonic and registers */
+ switch (op) {
+ case 0: /* AND */
+ case 1: /* EOR */
+ case 2: /* LSL */
+ case 3: /* LSR */
+ case 4: /* ASR */
+ case 7: /* ROR */
+ case 9: /* NEG */
+ case 12: /* ORR */
+ case 13: /* MUL */
+ case 15: /* MVN */
+ UnwPrintd8("%s r%d ,r%d\t; r%d %s, r%d %s",mnu[op],rd, rs, rd, M_Origin2Str(state->regData[rd].o), rs, M_Origin2Str(state->regData[rs].o));
+ break;
+
+ case 5: /* ADC */
+ case 6: /* SBC */
+ UnwPrintd4("%s r%d, r%d", mnu[op], rd, rs);
+ break;
+
+ case 8: /* TST */
+ case 10: /* CMP */
+ case 11: /* CMN */
+ /* Irrelevant to unwinding */
+ UnwPrintd2("%s ???", mnu[op]);
+ break;
+
+ case 14: /* BIC */
+ UnwPrintd5("r%d ,r%d\t; r%d %s", rd, rs, rs, M_Origin2Str(state->regData[rs].o));
+ break;
+ }
+
+ /* Perform operation */
+ switch (op) {
+ case 0: /* AND */
+ state->regData[rd].v &= state->regData[rs].v;
+ break;
+
+ case 1: /* EOR */
+ state->regData[rd].v ^= state->regData[rs].v;
+ break;
+
+ case 2: /* LSL */
+ state->regData[rd].v <<= state->regData[rs].v;
+ break;
+
+ case 3: /* LSR */
+ state->regData[rd].v >>= state->regData[rs].v;
+ break;
+
+ case 4: /* ASR */
+ if (state->regData[rd].v & 0x80000000) {
+ state->regData[rd].v >>= state->regData[rs].v;
+ state->regData[rd].v |= 0xFFFFFFFF << (32 - state->regData[rs].v);
+ }
+ else {
+ state->regData[rd].v >>= state->regData[rs].v;
+ }
+
+ break;
+
+ case 5: /* ADC */
+ case 6: /* SBC */
+ case 8: /* TST */
+ case 10: /* CMP */
+ case 11: /* CMN */
+ break;
+
+ case 7: /* ROR */
+ state->regData[rd].v = (state->regData[rd].v >> state->regData[rs].v) |
+ (state->regData[rd].v << (32 - state->regData[rs].v));
+ break;
+
+ case 9: /* NEG */
+ state->regData[rd].v = -state->regData[rs].v;
+ break;
+
+ case 12: /* ORR */
+ state->regData[rd].v |= state->regData[rs].v;
+ break;
+
+ case 13: /* MUL */
+ state->regData[rd].v *= state->regData[rs].v;
+ break;
+
+ case 14: /* BIC */
+ state->regData[rd].v &= ~state->regData[rs].v;
+ break;
+
+ case 15: /* MVN */
+ state->regData[rd].v = ~state->regData[rs].v;
+ break;
+ }
+
+ /* Propagate data origins */
+ switch (op) {
+ case 0: /* AND */
+ case 1: /* EOR */
+ case 2: /* LSL */
+ case 3: /* LSR */
+ case 4: /* ASR */
+ case 7: /* ROR */
+ case 12: /* ORR */
+ case 13: /* MUL */
+ case 14: /* BIC */
+ if (M_IsOriginValid(state->regData[rd].o) && M_IsOriginValid(state->regData[rs].o)) {
+ state->regData[rd].o = state->regData[rs].o;
+ state->regData[rd].o |= REG_VAL_ARITHMETIC;
+ }
+ else
+ state->regData[rd].o = REG_VAL_INVALID;
+ break;
+
+ case 5: /* ADC */
+ case 6: /* SBC */
+ /* C-bit not tracked */
+ state->regData[rd].o = REG_VAL_INVALID;
+ break;
+
+ case 8: /* TST */
+ case 10: /* CMP */
+ case 11: /* CMN */
+ /* Nothing propagated */
+ break;
+
+ case 9: /* NEG */
+ case 15: /* MVN */
+ state->regData[rd].o = state->regData[rs].o;
+ state->regData[rd].o |= REG_VAL_ARITHMETIC;
+ break;
+ }
+ }
+ /* Format 5: Hi register operations/branch exchange
+ * ADD Rd, Hs
+ * CMP Hd, Rs
+ * MOV Hd, Hs
+ */
+ else if ((instr & 0xFC00) == 0x4400) {
+ uint8_t op = (instr & 0x0300) >> 8;
+ bool h1 = (instr & 0x0080) ? true: false;
+ bool h2 = (instr & 0x0040) ? true: false;
+ uint8_t rhs = (instr & 0x0038) >> 3;
+ uint8_t rhd = (instr & 0x0007);
+
+ /* Adjust the register numbers */
+ if (h2) rhs += 8;
+ if (h1) rhd += 8;
+
+ switch (op) {
+ case 0: /* ADD */
+ UnwPrintd5("ADD r%d, r%d\t; r%d %s", rhd, rhs, rhs, M_Origin2Str(state->regData[rhs].o));
+ state->regData[rhd].v += state->regData[rhs].v;
+ state->regData[rhd].o = state->regData[rhs].o;
+ state->regData[rhd].o |= REG_VAL_ARITHMETIC;
+ break;
+
+ case 1: /* CMP */
+ /* Irrelevant to unwinding */
+ UnwPrintd1("CMP ???");
+ break;
+
+ case 2: /* MOV */
+ UnwPrintd5("MOV r%d, r%d\t; r%d %s", rhd, rhs, rhd, M_Origin2Str(state->regData[rhs].o));
+ state->regData[rhd].v = state->regData[rhs].v;
+ state->regData[rhd].o = state->regData[rhs].o;
+ break;
+
+ case 3: /* BX */
+ UnwPrintd4("BX r%d\t; r%d %s\n", rhs, rhs, M_Origin2Str(state->regData[rhs].o));
+
+ /* Only follow BX if the data was from the stack or BX LR */
+ if (rhs == 14 || state->regData[rhs].o == REG_VAL_FROM_STACK) {
+ UnwPrintd2(" Return PC=0x%x\n", state->regData[rhs].v & (~0x1));
+
+ /* Report the return address, including mode bit */
+ if (!UnwReportRetAddr(state, state->regData[rhs].v))
+ return UNWIND_TRUNCATED;
+
+ /* Update the PC */
+ state->regData[15].v = state->regData[rhs].v;
+
+ /* Determine the new mode */
+ if (state->regData[rhs].v & 0x1) {
+ /* Branching to THUMB */
+
+ /* Account for the auto-increment which isn't needed */
+ state->regData[15].v -= 2;
+ }
+ else /* Branch to ARM */
+ return UnwStartArm(state);
+ }
+ else {
+ UnwPrintd4("\nError: BX to invalid register: r%d = 0x%x (%s)\n", rhs, state->regData[rhs].o, M_Origin2Str(state->regData[rhs].o));
+ return UNWIND_FAILURE;
+ }
+ }
+ }
+ /* Format 9: PC-relative load
+ * LDR Rd,[PC, #imm]
+ */
+ else if ((instr & 0xF800) == 0x4800) {
+ uint8_t rd = (instr & 0x0700) >> 8;
+ uint8_t word8 = (instr & 0x00FF);
+ uint32_t address;
+
+ /* Compute load address, adding a word to account for prefetch */
+ address = (state->regData[15].v & (~0x3)) + 4 + (word8 << 2);
+
+ UnwPrintd3("LDR r%d, 0x%08x", rd, address);
+
+ if (!UnwMemReadRegister(state, address, &state->regData[rd]))
+ return UNWIND_DREAD_W_FAIL;
+ }
+ /* Format 13: add offset to Stack Pointer
+ * ADD sp,#+imm
+ * ADD sp,#-imm
+ */
+ else if ((instr & 0xFF00) == 0xB000) {
+ uint8_t value = (instr & 0x7F) * 4;
+
+ /* Check the negative bit */
+ if ((instr & 0x80) != 0) {
+ UnwPrintd2("SUB sp,#0x%x", value);
+ state->regData[13].v -= value;
+ }
+ else {
+ UnwPrintd2("ADD sp,#0x%x", value);
+ state->regData[13].v += value;
+ }
+ }
+ /* Format 14: push/pop registers
+ * PUSH {Rlist}
+ * PUSH {Rlist, LR}
+ * POP {Rlist}
+ * POP {Rlist, PC}
+ */
+ else if ((instr & 0xF600) == 0xB400) {
+ bool L = !!(instr & 0x0800);
+ bool R = !!(instr & 0x0100);
+ uint8_t rList = (instr & 0x00FF);
+
+ if (L) {
+ uint8_t r;
+
+ /* Load from memory: POP */
+ UnwPrintd2("POP {Rlist%s}\n", R ? ", PC" : "");
+
+ for (r = 0; r < 8; r++) {
+ if (rList & (0x1 << r)) {
+
+ /* Read the word */
+ if (!UnwMemReadRegister(state, state->regData[13].v, &state->regData[r]))
+ return UNWIND_DREAD_W_FAIL;
+
+ /* Alter the origin to be from the stack if it was valid */
+ if (M_IsOriginValid(state->regData[r].o))
+ state->regData[r].o = REG_VAL_FROM_STACK;
+
+ state->regData[13].v += 4;
+
+ UnwPrintd3(" r%d = 0x%08x\n", r, state->regData[r].v);
+ }
+ }
+
+ /* Check if the PC is to be popped */
+ if (R) {
+ /* Get the return address */
+ if (!UnwMemReadRegister(state, state->regData[13].v, &state->regData[15]))
+ return UNWIND_DREAD_W_FAIL;
+
+ /* Alter the origin to be from the stack if it was valid */
+ if (!M_IsOriginValid(state->regData[15].o)) {
+ /* Return address is not valid */
+ UnwPrintd1("PC popped with invalid address\n");
+ return UNWIND_FAILURE;
+ }
+ else {
+ /* The bottom bit should have been set to indicate that
+ * the caller was from Thumb. This would allow return
+ * by BX for interworking APCS.
+ */
+ if ((state->regData[15].v & 0x1) == 0) {
+ UnwPrintd2("Warning: Return address not to Thumb: 0x%08x\n", state->regData[15].v);
+
+ /* Pop into the PC will not switch mode */
+ return UNWIND_INCONSISTENT;
+ }
+
+ /* Store the return address */
+ if (!UnwReportRetAddr(state, state->regData[15].v))
+ return UNWIND_TRUNCATED;
+
+ /* Now have the return address */
+ UnwPrintd2(" Return PC=%x\n", state->regData[15].v);
+
+ /* Update the pc */
+ state->regData[13].v += 4;
+
+ /* Compensate for the auto-increment, which isn't needed here */
+ state->regData[15].v -= 2;
+ }
+ }
+ }
+ else {
+ int8_t r;
+
+ /* Store to memory: PUSH */
+ UnwPrintd2("PUSH {Rlist%s}", R ? ", LR" : "");
+
+ /* Check if the LR is to be pushed */
+ if (R) {
+ UnwPrintd3("\n lr = 0x%08x\t; %s", state->regData[14].v, M_Origin2Str(state->regData[14].o));
+
+ state->regData[13].v -= 4;
+
+ /* Write the register value to memory */
+ if (!UnwMemWriteRegister(state, state->regData[13].v, &state->regData[14]))
+ return UNWIND_DWRITE_W_FAIL;
+ }
+
+ for (r = 7; r >= 0; r--) {
+ if (rList & (0x1 << r)) {
+ UnwPrintd4("\n r%d = 0x%08x\t; %s", r, state->regData[r].v, M_Origin2Str(state->regData[r].o));
+
+ state->regData[13].v -= 4;
+
+ if (!UnwMemWriteRegister(state, state->regData[13].v, &state->regData[r]))
+ return UNWIND_DWRITE_W_FAIL;
+ }
+ }
+ }
+ }
+
+ /*
+ * Conditional branches
+ * Bcond
+ */
+ else if ((instr & 0xF000) == 0xD000) {
+ int32_t branchValue = (instr & 0xFF);
+ if (branchValue & 0x80) branchValue |= 0xFFFFFF00;
+
+ /* Branch distance is twice that specified in the instruction. */
+ branchValue *= 2;
+
+ UnwPrintd2("Bcond %d \n", branchValue);
+
+ /* Only take the branch if a loop was detected */
+ if (loopDetected) {
+
+ /* Update PC */
+ state->regData[15].v += branchValue;
+
+ /* Need to advance by a word to account for pre-fetch.
+ * Advance by a half word here, allowing the normal address
+ * advance to account for the other half word.
+ */
+ state->regData[15].v += 2;
+
+ /* Display PC of next instruction */
+ UnwPrintd2(" New PC=%x", state->regData[15].v + 2);
+ }
+ }
+
+ /* Format 18: unconditional branch
+ * B label
+ */
+ else if ((instr & 0xF800) == 0xE000) {
+ uint32_t v;
+ int32_t branchValue = signExtend11(instr & 0x07FF);
+
+ /* Branch distance is twice that specified in the instruction. */
+ branchValue *= 2;
+
+ UnwPrintd2("B %d \n", branchValue);
+
+ /* Update PC */
+ state->regData[15].v += branchValue;
+
+ /* Need to advance by a word to account for pre-fetch.
+ * Advance by a half word here, allowing the normal address
+ * advance to account for the other half word.
+ */
+ state->regData[15].v += 2;
+
+ /* Compute the jump address */
+ v = state->regData[15].v + 2;
+
+ /* Display PC of next instruction */
+ UnwPrintd2(" New PC=%x", v);
+
+ /* Did we detect an infinite loop ? */
+ loopDetected = lastJumpAddr == v;
+
+ /* Remember the last address we jumped to */
+ lastJumpAddr = v;
+ }
+ else {
+ UnwPrintd1("????");
+
+ /* Unknown/undecoded. May alter some register, so invalidate file */
+ UnwInvalidateRegisterFile(state->regData);
+ }
+
+ UnwPrintd1("\n");
+
+ /* Should never hit the reset vector */
+ if (state->regData[15].v == 0) return UNWIND_RESET;
+
+ /* Check next address */
+ state->regData[15].v += 2;
+
+ /* Garbage collect the memory hash (used only for the stack) */
+ UnwMemHashGC(state);
+
+ if (--t == 0) return UNWIND_EXHAUSTED;
+
+ }
+
+ return UNWIND_SUCCESS;
+}
+
+#endif // __arm__ || __thumb__
diff --git a/Marlin/src/HAL/shared/backtrace/unwarmbytab.cpp b/Marlin/src/HAL/shared/backtrace/unwarmbytab.cpp
new file mode 100644
index 0000000000..148927a19f
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/unwarmbytab.cpp
@@ -0,0 +1,430 @@
+/*
+ * Libbacktrace
+ * Copyright 2015 Stephen Street
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://www.mozilla.org/en-US/MPL/2.0/
+ *
+ * This library was modified, some bugs fixed, stack address validated
+ * and adapted to be used in Marlin 3D printer firmware as backtracer
+ * for exceptions for debugging purposes in 2018 by Eduardo José Tagle.
+ */
+
+#if defined(__arm__) || defined(__thumb__)
+
+#include "unwarmbytab.h"
+
+#include
+#include
+
+/* These symbols point to the unwind index and should be provide by the linker script */
+extern "C" const UnwTabEntry __exidx_start[];
+extern "C" const UnwTabEntry __exidx_end[];
+
+/* This prevents the linking of libgcc unwinder code */
+void __aeabi_unwind_cpp_pr0() {};
+void __aeabi_unwind_cpp_pr1() {};
+void __aeabi_unwind_cpp_pr2() {};
+
+static inline __attribute__((always_inline)) uint32_t prel31_to_addr(const uint32_t *prel31) {
+ uint32_t offset = (((uint32_t)(*prel31)) << 1) >> 1;
+ return ((uint32_t)prel31 + offset) & 0x7FFFFFFF;
+}
+
+static const UnwTabEntry *UnwTabSearchIndex(const UnwTabEntry *start, const UnwTabEntry *end, uint32_t ip) {
+ const UnwTabEntry *middle;
+
+ /* Perform a binary search of the unwind index */
+ while (start < end - 1) {
+ middle = start + ((end - start + 1) >> 1);
+ if (ip < prel31_to_addr(&middle->addr_offset))
+ end = middle;
+ else
+ start = middle;
+ }
+ return start;
+}
+
+/*
+ * Get the function name or nullptr if not found
+ */
+static const char *UnwTabGetFunctionName(const UnwindCallbacks *cb, uint32_t address) {
+ uint32_t flag_word = 0;
+ if (!cb->readW(address-4,&flag_word))
+ return nullptr;
+
+ if ((flag_word & 0xFF000000) == 0xFF000000) {
+ return (const char *)(address - 4 - (flag_word & 0x00FFFFFF));
+ }
+ return nullptr;
+}
+
+/**
+ * Get the next frame unwinding instruction
+ *
+ * Return either the instruction or -1 to signal no more instructions
+ * are available
+ */
+static int UnwTabGetNextInstruction(const UnwindCallbacks *cb, UnwTabState *ucb) {
+ int instruction;
+
+ /* Are there more instructions */
+ if (ucb->remaining == 0)
+ return -1;
+
+ /* Extract the current instruction */
+ uint32_t v = 0;
+ if (!cb->readW(ucb->current, &v))
+ return -1;
+ instruction = (v >> (ucb->byte << 3)) & 0xFF;
+
+ /* Move the next byte */
+ --ucb->byte;
+ if (ucb->byte < 0) {
+ ucb->current += 4;
+ ucb->byte = 3;
+ }
+ --ucb->remaining;
+
+ return instruction;
+}
+
+/**
+ * Initialize the frame unwinding state
+ */
+static UnwResult UnwTabStateInit(const UnwindCallbacks *cb, UnwTabState *ucb, uint32_t instructions, const UnwindFrame *frame) {
+
+ /* Initialize control block */
+ memset(ucb, 0, sizeof(UnwTabState));
+ ucb->current = instructions;
+
+ /* Is a short unwind description */
+ uint32_t v = 0;
+ if (!cb->readW(instructions, &v))
+ return UNWIND_DREAD_W_FAIL;
+
+ if ((v & 0xFF000000) == 0x80000000) {
+ ucb->remaining = 3;
+ ucb->byte = 2;
+ /* Is a long unwind description */
+ } else if ((v & 0xFF000000) == 0x81000000) {
+ ucb->remaining = ((v & 0x00FF0000) >> 14) + 2;
+ ucb->byte = 1;
+ } else
+ return UNWIND_UNSUPPORTED_DWARF_PERSONALITY;
+
+ /* Initialize the virtual register set */
+ ucb->vrs[7] = frame->fp;
+ ucb->vrs[13] = frame->sp;
+ ucb->vrs[14] = frame->lr;
+ ucb->vrs[15] = 0;
+
+ /* All good */
+ return UNWIND_SUCCESS;
+}
+
+/*
+ * Execute unwinding instructions
+ */
+static UnwResult UnwTabExecuteInstructions(const UnwindCallbacks *cb, UnwTabState *ucb) {
+ int instruction;
+ uint32_t mask, reg, vsp;
+
+ /* Consume all instruction byte */
+ while ((instruction = UnwTabGetNextInstruction(cb, ucb)) != -1) {
+
+ if ((instruction & 0xC0) == 0x00) { // ARM_EXIDX_CMD_DATA_POP
+ /* vsp += (xxxxxx << 2) + 4 */
+ ucb->vrs[13] += ((instruction & 0x3F) << 2) + 4;
+ }
+ else if ((instruction & 0xC0) == 0x40) { // ARM_EXIDX_CMD_DATA_PUSH
+ /* vsp -= (xxxxxx << 2) - 4 */
+ ucb->vrs[13] -= ((instruction & 0x3F) << 2) - 4;
+ }
+ else if ((instruction & 0xF0) == 0x80) {
+ /* pop under mask {r15-r12},{r11-r4} or refuse to unwind */
+ instruction = instruction << 8 | UnwTabGetNextInstruction(cb, ucb);
+
+ /* Check for refuse to unwind */
+ if (instruction == 0x8000) // ARM_EXIDX_CMD_REFUSED
+ return UNWIND_REFUSED;
+
+ /* Pop registers using mask */ // ARM_EXIDX_CMD_REG_POP
+ vsp = ucb->vrs[13];
+ mask = instruction & 0xFFF;
+
+ reg = 4;
+ while (mask) {
+ if ((mask & 1) != 0) {
+ uint32_t v;
+ if (!cb->readW(vsp,&v))
+ return UNWIND_DREAD_W_FAIL;
+ ucb->vrs[reg] = v;
+ v += 4;
+ }
+ mask >>= 1;
+ ++reg;
+ }
+
+ /* Patch up the vrs sp if it was in the mask */
+ if (instruction & (1 << (13 - 4)))
+ ucb->vrs[13] = vsp;
+ }
+ else if ((instruction & 0xF0) == 0x90 // ARM_EXIDX_CMD_REG_TO_SP
+ && instruction != 0x9D
+ && instruction != 0x9F
+ ) {
+ /* vsp = r[nnnn] */
+ ucb->vrs[13] = ucb->vrs[instruction & 0x0F];
+ }
+ else if ((instruction & 0xF0) == 0xA0) { // ARM_EXIDX_CMD_REG_POP
+ /* pop r4-r[4+nnn] or pop r4-r[4+nnn], r14*/
+ vsp = ucb->vrs[13];
+
+ for (reg = 4; reg <= uint32_t((instruction & 0x07) + 4); ++reg) {
+ uint32_t v;
+ if (!cb->readW(vsp,&v))
+ return UNWIND_DREAD_W_FAIL;
+
+ ucb->vrs[reg] = v;
+ vsp += 4;
+ }
+
+ if (instruction & 0x08) { // ARM_EXIDX_CMD_REG_POP
+ uint32_t v;
+ if (!cb->readW(vsp,&v))
+ return UNWIND_DREAD_W_FAIL;
+ ucb->vrs[14] = v;
+ vsp += 4;
+ }
+
+ ucb->vrs[13] = vsp;
+
+ }
+ else if (instruction == 0xB0) { // ARM_EXIDX_CMD_FINISH
+ /* finished */
+ if (ucb->vrs[15] == 0)
+ ucb->vrs[15] = ucb->vrs[14];
+
+ /* All done unwinding */
+ return UNWIND_SUCCESS;
+
+ }
+ else if (instruction == 0xB1) { // ARM_EXIDX_CMD_REG_POP
+ /* pop register under mask {r3,r2,r1,r0} */
+ vsp = ucb->vrs[13];
+ mask = UnwTabGetNextInstruction(cb, ucb);
+
+ reg = 0;
+ while (mask) {
+ if ((mask & 1) != 0) {
+ uint32_t v;
+ if (!cb->readW(vsp,&v))
+ return UNWIND_DREAD_W_FAIL;
+
+ ucb->vrs[reg] = v;
+ vsp += 4;
+ }
+ mask >>= 1;
+ ++reg;
+ }
+ ucb->vrs[13] = (uint32_t)vsp;
+
+ }
+ else if (instruction == 0xB2) { // ARM_EXIDX_CMD_DATA_POP
+ /* vps = vsp + 0x204 + (uleb128 << 2) */
+ ucb->vrs[13] += 0x204 + (UnwTabGetNextInstruction(cb, ucb) << 2);
+ }
+ else if (instruction == 0xB3 // ARM_EXIDX_CMD_VFP_POP
+ || instruction == 0xC8
+ || instruction == 0xC9
+ ) {
+ /* pop VFP double-precision registers */
+ vsp = ucb->vrs[13];
+
+ /* D[ssss]-D[ssss+cccc] */
+ uint32_t v;
+ if (!cb->readW(vsp,&v))
+ return UNWIND_DREAD_W_FAIL;
+
+ ucb->vrs[14] = v;
+ vsp += 4;
+
+ if (instruction == 0xC8) {
+ /* D[16+sssss]-D[16+ssss+cccc] */
+ ucb->vrs[14] |= 1 << 16;
+ }
+
+ if (instruction != 0xB3) {
+ /* D[sssss]-D[ssss+cccc] */
+ ucb->vrs[14] |= 1 << 17;
+ }
+
+ ucb->vrs[13] = vsp;
+ }
+ else if ((instruction & 0xF8) == 0xB8 || (instruction & 0xF8) == 0xD0) {
+ /* Pop VFP double precision registers D[8]-D[8+nnn] */
+ ucb->vrs[14] = 0x80 | (instruction & 0x07);
+ if ((instruction & 0xF8) == 0xD0)
+ ucb->vrs[14] = 1 << 17;
+ }
+ else
+ return UNWIND_UNSUPPORTED_DWARF_INSTR;
+ }
+ return UNWIND_SUCCESS;
+}
+
+static inline __attribute__((always_inline)) uint32_t read_psp() {
+ /* Read the current PSP and return its value as a pointer */
+ uint32_t psp;
+
+ __asm__ volatile (
+ " mrs %0, psp \n"
+ : "=r" (psp) : :
+ );
+
+ return psp;
+}
+
+/*
+ * Unwind the specified frame and goto the previous one
+ */
+static UnwResult UnwTabUnwindFrame(const UnwindCallbacks *cb, UnwindFrame *frame) {
+
+ UnwResult err;
+ UnwTabState ucb;
+ const UnwTabEntry *index;
+ uint32_t instructions;
+
+ /* Search the unwind index for the matching unwind table */
+ index = UnwTabSearchIndex(__exidx_start, __exidx_end, frame->pc);
+
+ /* Make sure we can unwind this frame */
+ if (index->insn == 0x00000001)
+ return UNWIND_SUCCESS;
+
+ /* Get the pointer to the first unwind instruction */
+ if (index->insn & 0x80000000)
+ instructions = (uint32_t)&index->insn;
+ else
+ instructions = prel31_to_addr(&index->insn);
+
+ /* Initialize the unwind control block */
+ if ((err = UnwTabStateInit(cb, &ucb, instructions, frame)) < 0)
+ return err;
+
+ /* Execute the unwind instructions */
+ err = UnwTabExecuteInstructions(cb, &ucb);
+ if (err < 0)
+ return err;
+
+ /* Set the virtual pc to the virtual lr if this is the first unwind */
+ if (ucb.vrs[15] == 0)
+ ucb.vrs[15] = ucb.vrs[14];
+
+ /* Check for exception return */
+ /* TODO Test with other ARM processors to verify this method. */
+ if ((ucb.vrs[15] & 0xF0000000) == 0xF0000000) {
+ /* According to the Cortex Programming Manual (p.44), the stack address is always 8-byte aligned (Cortex-M7).
+ Depending on where the exception came from (MSP or PSP), we need the right SP value to work with.
+
+ ucb.vrs[7] contains the right value, so take it and align it by 8 bytes, store it as the current
+ SP to work with (ucb.vrs[13]) which is then saved as the current (virtual) frame's SP.
+ */
+ uint32_t stack;
+ ucb.vrs[13] = (ucb.vrs[7] & ~7);
+
+ /* If we need to start from the MSP, we need to go down X words to find the PC, where:
+ X=2 if it was a non-floating-point exception
+ X=20 if it was a floating-point (VFP) exception
+
+ If we need to start from the PSP, we need to go up exactly 6 words to find the PC.
+ See the ARMv7-M Architecture Reference Manual p.594 and Cortex-M7 Processor Programming Manual p.44/p.45 for details.
+ */
+ if ((ucb.vrs[15] & 0xC) == 0) {
+ /* Return to Handler Mode: MSP (0xFFFFFF-1) */
+ stack = ucb.vrs[13];
+
+ /* The PC is always 2 words down from the MSP, if it was a non-floating-point exception */
+ stack -= 2*4;
+
+ /* If there was a VFP exception (0xFFFFFFE1), the PC is located another 18 words down */
+ if ((ucb.vrs[15] & 0xF0) == 0xE0) {
+ stack -= 18*4;
+ }
+ }
+ else {
+ /* Return to Thread Mode: PSP (0xFFFFFF-d) */
+ stack = read_psp();
+
+ /* The PC is always 6 words up from the PSP */
+ stack += 6*4;
+ }
+
+ /* Store the PC */
+ uint32_t v;
+ if (!cb->readW(stack,&v))
+ return UNWIND_DREAD_W_FAIL;
+ ucb.vrs[15] = v;
+ stack -= 4;
+
+ /* Store the LR */
+ if (!cb->readW(stack,&v))
+ return UNWIND_DREAD_W_FAIL;
+ ucb.vrs[14] = v;
+ stack -= 4;
+ }
+
+ /* We are done if current frame pc is equal to the virtual pc, prevent infinite loop */
+ if (frame->pc == ucb.vrs[15])
+ return UNWIND_SUCCESS;
+
+ /* Update the frame */
+ frame->fp = ucb.vrs[7];
+ frame->sp = ucb.vrs[13];
+ frame->lr = ucb.vrs[14];
+ frame->pc = ucb.vrs[15];
+
+ /* All good - Continue unwinding */
+ return UNWIND_MORE_AVAILABLE;
+}
+
+UnwResult UnwindByTableStart(UnwindFrame* frame, const UnwindCallbacks *cb, void *data) {
+
+ UnwResult err = UNWIND_SUCCESS;
+ UnwReport entry;
+
+ /* Use DWARF unwind information to unwind frames */
+ do {
+ if (frame->pc == 0) {
+ /* Reached __exidx_end. */
+ break;
+ }
+
+ if (frame->pc == 0x00000001) {
+ /* Reached .cantunwind instruction. */
+ break;
+ }
+
+ /* Find the unwind index of the current frame pc */
+ const UnwTabEntry *index = UnwTabSearchIndex(__exidx_start, __exidx_end, frame->pc);
+
+ /* Clear last bit (Thumb indicator) */
+ frame->pc &= 0xFFFFFFFEU;
+
+ /* Generate the backtrace information */
+ entry.address = frame->pc;
+ entry.function = prel31_to_addr(&index->addr_offset);
+ entry.name = UnwTabGetFunctionName(cb, entry.function);
+ if (!cb->report(data,&entry))
+ break;
+
+ /* Unwind frame and repeat */
+ } while ((err = UnwTabUnwindFrame(cb, frame)) == UNWIND_MORE_AVAILABLE);
+
+ /* All done */
+ return err;
+}
+
+#endif // __arm__ || __thumb__
diff --git a/Marlin/src/HAL/shared/backtrace/unwarmbytab.h b/Marlin/src/HAL/shared/backtrace/unwarmbytab.h
new file mode 100644
index 0000000000..53aeca2594
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/unwarmbytab.h
@@ -0,0 +1,31 @@
+/***************************************************************************
+ * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
+ * Updated, adapted and several bug fixes on 2018 by Eduardo José Tagle
+ *
+ * This program is PUBLIC DOMAIN.
+ * This means that there is no copyright and anyone is able to take a copy
+ * for free and use it as they wish, with or without modifications, and in
+ * any context, commercially or otherwise. The only limitation is that I
+ * don't guarantee that the software is fit for any purpose or accept any
+ * liability for its use or misuse - this software is without warranty.
+ ***************************************************************************
+ * File Description: Interface to the memory tracking sub-system.
+ **************************************************************************/
+
+#pragma once
+
+#include "unwarm.h"
+
+typedef struct {
+ uint32_t vrs[16];
+ uint32_t current; /* Address of current byte */
+ int remaining;
+ int byte;
+} UnwTabState;
+
+typedef struct {
+ uint32_t addr_offset;
+ uint32_t insn;
+} UnwTabEntry;
+
+UnwResult UnwindByTableStart(UnwindFrame* frame, const UnwindCallbacks *cb, void *data);
diff --git a/Marlin/src/HAL/shared/backtrace/unwarmmem.cpp b/Marlin/src/HAL/shared/backtrace/unwarmmem.cpp
new file mode 100644
index 0000000000..24023200e1
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/unwarmmem.cpp
@@ -0,0 +1,106 @@
+/***************************************************************************
+ * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
+ * Updated, adapted and several bug fixes on 2018 by Eduardo José Tagle
+ *
+ * This program is PUBLIC DOMAIN.
+ * This means that there is no copyright and anyone is able to take a copy
+ * for free and use it as they wish, with or without modifications, and in
+ * any context, commercially or otherwise. The only limitation is that I
+ * don't guarantee that the software is fit for any purpose or accept any
+ * liability for its use or misuse - this software is without warranty.
+ ***************************************************************************
+ * File Description: Implementation of the memory tracking sub-system.
+ **************************************************************************/
+
+#if defined(__arm__) || defined(__thumb__)
+#define MODULE_NAME "UNWARMMEM"
+
+#include
+#include "unwarmmem.h"
+#include "unwarm.h"
+
+#define M_IsIdxUsed(a, v) !!((a)[v >> 3] & (1 << (v & 0x7)))
+#define M_SetIdxUsed(a, v) ((a)[v >> 3] |= (1 << (v & 0x7)))
+#define M_ClrIdxUsed(a, v) ((a)[v >> 3] &= ~(1 << (v & 0x7)))
+
+/** Search the memory hash to see if an entry is stored in the hash already.
+ * This will search the hash and either return the index where the item is
+ * stored, or -1 if the item was not found.
+ */
+static int16_t memHashIndex(MemData * const memData, const uint32_t addr) {
+
+ const uint16_t v = addr % MEM_HASH_SIZE;
+ uint16_t s = v;
+
+ do {
+ /* Check if the element is occupied */
+ if (M_IsIdxUsed(memData->used, s)) {
+ /* Check if it is occupied with the sought data */
+ if (memData->a[s] == addr) return s;
+ }
+ else {
+ /* Item is free, this is where the item should be stored */
+ return s;
+ }
+
+ /* Search the next entry */
+ s++;
+ if (s > MEM_HASH_SIZE) s = 0;
+ } while (s != v);
+
+ /* Search failed, hash is full and the address not stored */
+ return -1;
+}
+
+bool UnwMemHashRead(MemData * const memData, uint32_t addr,uint32_t * const data, bool * const tracked) {
+
+ const int16_t i = memHashIndex(memData, addr);
+
+ if (i >= 0 && M_IsIdxUsed(memData->used, i) && memData->a[i] == addr) {
+ *data = memData->v[i];
+ *tracked = M_IsIdxUsed(memData->tracked, i);
+ return true;
+ }
+ else {
+ /* Address not found in the hash */
+ return false;
+ }
+}
+
+bool UnwMemHashWrite(MemData * const memData, uint32_t addr, uint32_t val, bool valValid) {
+ const int16_t i = memHashIndex(memData, addr);
+ if (i < 0) return false; /* Hash full */
+
+ /* Store the item */
+ memData->a[i] = addr;
+ M_SetIdxUsed(memData->used, i);
+
+ if (valValid) {
+ memData->v[i] = val;
+ M_SetIdxUsed(memData->tracked, i);
+ }
+ else {
+ #ifdef UNW_DEBUG
+ memData->v[i] = 0xDEADBEEF;
+ #endif
+ M_ClrIdxUsed(memData->tracked, i);
+ }
+
+ return true;
+}
+
+void UnwMemHashGC(UnwState * const state) {
+
+ const uint32_t minValidAddr = state->regData[13].v;
+ MemData * const memData = &state->memData;
+ uint16_t t;
+
+ for (t = 0; t < MEM_HASH_SIZE; t++) {
+ if (M_IsIdxUsed(memData->used, t) && (memData->a[t] < minValidAddr)) {
+ UnwPrintd3("MemHashGC: Free elem %d, addr 0x%08x\n", t, memData->a[t]);
+ M_ClrIdxUsed(memData->used, t);
+ }
+ }
+}
+
+#endif // __arm__ || __thumb__
diff --git a/Marlin/src/HAL/shared/backtrace/unwarmmem.h b/Marlin/src/HAL/shared/backtrace/unwarmmem.h
new file mode 100644
index 0000000000..eb4579a761
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/unwarmmem.h
@@ -0,0 +1,21 @@
+/***************************************************************************
+ * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
+ * Updated, adapted and several bug fixes on 2018 by Eduardo José Tagle
+ *
+ * This program is PUBLIC DOMAIN.
+ * This means that there is no copyright and anyone is able to take a copy
+ * for free and use it as they wish, with or without modifications, and in
+ * any context, commercially or otherwise. The only limitation is that I
+ * don't guarantee that the software is fit for any purpose or accept any
+ * liability for its use or misuse - this software is without warranty.
+ ***************************************************************************
+ * File Description: Interface to the memory tracking sub-system.
+ **************************************************************************/
+
+#pragma once
+
+#include "unwarm.h"
+
+bool UnwMemHashRead(MemData * const memData, uint32_t addr, uint32_t * const data, bool * const tracked);
+bool UnwMemHashWrite(MemData * const memData, uint32_t addr, uint32_t val, bool valValid);
+void UnwMemHashGC(UnwState * const state);
diff --git a/Marlin/src/HAL/shared/backtrace/unwinder.cpp b/Marlin/src/HAL/shared/backtrace/unwinder.cpp
new file mode 100644
index 0000000000..aedfa2404d
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/unwinder.cpp
@@ -0,0 +1,52 @@
+/***************************************************************************
+ * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
+ * Updated, adapted and several bug fixes on 2018 by Eduardo José Tagle
+ *
+ * This program is PUBLIC DOMAIN.
+ * This means that there is no copyright and anyone is able to take a copy
+ * for free and use it as they wish, with or without modifications, and in
+ * any context, commercially or otherwise. The only limitation is that I
+ * don't guarantee that the software is fit for any purpose or accept any
+ * liability for its use or misuse - this software is without warranty.
+ ***************************************************************************
+ * File Description: Implementation of the interface into the ARM unwinder.
+ **************************************************************************/
+
+#if defined(__arm__) || defined(__thumb__)
+
+#define MODULE_NAME "UNWINDER"
+
+#include
+#include
+#include "unwinder.h"
+#include "unwarm.h"
+#include "unwarmbytab.h"
+
+/* These symbols point to the unwind index and should be provide by the linker script */
+extern "C" const UnwTabEntry __exidx_start[];
+extern "C" const UnwTabEntry __exidx_end[];
+
+// Detect if unwind information is present or not
+static int HasUnwindTableInfo() {
+ // > 16 because there are default entries we can't suppress
+ return ((char*)(&__exidx_end) - (char*)(&__exidx_start)) > 16 ? 1 : 0;
+}
+
+UnwResult UnwindStart(UnwindFrame* frame, const UnwindCallbacks *cb, void *data) {
+ if (HasUnwindTableInfo()) {
+ /* We have unwind information tables */
+ return UnwindByTableStart(frame, cb, data);
+ }
+ else {
+
+ /* We don't have unwind information tables */
+ UnwState state;
+
+ /* Initialize the unwinding state */
+ UnwInitState(&state, cb, data, frame->pc, frame->sp);
+
+ /* Check the Thumb bit */
+ return (frame->pc & 0x1) ? UnwStartThumb(&state) : UnwStartArm(&state);
+ }
+}
+#endif
diff --git a/Marlin/src/HAL/shared/backtrace/unwinder.h b/Marlin/src/HAL/shared/backtrace/unwinder.h
new file mode 100644
index 0000000000..9280e2f36e
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/unwinder.h
@@ -0,0 +1,172 @@
+/***************************************************************************
+ * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
+ * Updated, adapted and several bug fixes on 2018 by Eduardo José Tagle
+ *
+ * This program is PUBLIC DOMAIN.
+ * This means that there is no copyright and anyone is able to take a copy
+ * for free and use it as they wish, with or without modifications, and in
+ * any context, commercially or otherwise. The only limitation is that I
+ * don't guarantee that the software is fit for any purpose or accept any
+ * liability for its use or misuse - this software is without warranty.
+ **************************************************************************/
+/** \file
+ * Interface to the ARM stack unwinding module.
+ **************************************************************************/
+
+#pragma once
+
+#include
+
+/** \def UNW_DEBUG
+ * If this define is set, additional information will be produced while
+ * unwinding the stack to allow debug of the unwind module itself.
+ */
+/* #define UNW_DEBUG 1 */
+
+/***************************************************************************
+ * Type Definitions
+ **************************************************************************/
+
+/** Possible results for UnwindStart to return.
+ */
+typedef enum {
+ /** Unwinding was successful and complete. */
+ UNWIND_SUCCESS = 0,
+
+ /** Not an error: More frames are available. */
+ UNWIND_MORE_AVAILABLE = 1,
+
+ /** Unsupported DWARF unwind personality. */
+ UNWIND_UNSUPPORTED_DWARF_PERSONALITY = -1,
+
+ /** Refused to perform unwind. */
+ UNWIND_REFUSED = -2,
+
+ /** Reached an invalid SP. */
+ UNWIND_INVALID_SP = -3,
+
+ /** Reached an invalid PC */
+ UNWIND_INVALID_PC = -4,
+
+ /** Unsupported DWARF instruction */
+ UNWIND_UNSUPPORTED_DWARF_INSTR = -5,
+
+ /** More than UNW_MAX_INSTR_COUNT instructions were interpreted. */
+ UNWIND_EXHAUSTED = -6,
+
+ /** Unwinding stopped because the reporting func returned false. */
+ UNWIND_TRUNCATED = -7,
+
+ /** Read data was found to be inconsistent. */
+ UNWIND_INCONSISTENT = -8,
+
+ /** Unsupported instruction or data found. */
+ UNWIND_UNSUPPORTED = -9,
+
+ /** General failure. */
+ UNWIND_FAILURE = -10,
+
+ /** Illegal instruction. */
+ UNWIND_ILLEGAL_INSTR = -11,
+
+ /** Unwinding hit the reset vector. */
+ UNWIND_RESET = -12,
+
+ /** Failed read for an instruction word. */
+ UNWIND_IREAD_W_FAIL = -13,
+
+ /** Failed read for an instruction half-word. */
+ UNWIND_IREAD_H_FAIL = -14,
+
+ /** Failed read for an instruction byte. */
+ UNWIND_IREAD_B_FAIL = -15,
+
+ /** Failed read for a data word. */
+ UNWIND_DREAD_W_FAIL = -16,
+
+ /** Failed read for a data half-word. */
+ UNWIND_DREAD_H_FAIL = -17,
+
+ /** Failed read for a data byte. */
+ UNWIND_DREAD_B_FAIL = -18,
+
+ /** Failed write for a data word. */
+ UNWIND_DWRITE_W_FAIL = -19
+
+} UnwResult;
+
+/** A backtrace report */
+typedef struct {
+ uint32_t function; /** Starts address of function */
+ const char *name; /** Function name, or null if not available */
+ uint32_t address; /** PC on that function */
+} UnwReport;
+
+/** Type for function pointer for result callback.
+ * The function is passed two parameters, the first is a void * pointer,
+ * and the second is the return address of the function. The bottom bit
+ * of the passed address indicates the execution mode; if it is set,
+ * the execution mode at the return address is Thumb, otherwise it is
+ * ARM.
+ *
+ * The return value of this function determines whether unwinding should
+ * continue or not. If true is returned, unwinding will continue and the
+ * report function maybe called again in future. If false is returned,
+ * unwinding will stop with UnwindStart() returning UNWIND_TRUNCATED.
+ */
+typedef bool (*UnwindReportFunc)(void *data, const UnwReport *bte);
+
+/** Structure that holds memory callback function pointers.
+ */
+typedef struct {
+
+ /** Report an unwind result. */
+ UnwindReportFunc report;
+
+ /** Read a 32 bit word from memory.
+ * The memory address to be read is passed as \a address, and
+ * \a *val is expected to be populated with the read value.
+ * If the address cannot or should not be read, false can be
+ * returned to indicate that unwinding should stop. If true
+ * is returned, \a *val is assumed to be valid and unwinding
+ * will continue.
+ */
+ bool (*readW)(const uint32_t address, uint32_t *val);
+
+ /** Read a 16 bit half-word from memory.
+ * This function has the same usage as for readW, but is expected
+ * to read only a 16 bit value.
+ */
+ bool (*readH)(const uint32_t address, uint16_t *val);
+
+ /** Read a byte from memory.
+ * This function has the same usage as for readW, but is expected
+ * to read only an 8 bit value.
+ */
+ bool (*readB)(const uint32_t address, uint8_t *val);
+
+ #ifdef UNW_DEBUG
+ /** Print a formatted line for debug. */
+ void (*printf)(const char *format, ...);
+ #endif
+} UnwindCallbacks;
+
+/* A frame */
+typedef struct {
+ uint32_t fp;
+ uint32_t sp;
+ uint32_t lr;
+ uint32_t pc;
+} UnwindFrame;
+
+/** Start unwinding the current stack.
+ * This will unwind the stack starting at the PC value supplied to in the
+ * link register (i.e. not a normal register) and the stack pointer value
+ * supplied.
+ *
+ * -If the program was compiled with -funwind-tables it will use them to
+ * perform the traceback. Otherwise, brute force will be employed
+ * -If the program was compiled with -mpoke-function-name, then you will
+ * get function names in the traceback. Otherwise, you will not.
+ */
+UnwResult UnwindStart(UnwindFrame* frame, const UnwindCallbacks *cb, void *data);
diff --git a/Marlin/src/HAL/shared/backtrace/unwmemaccess.cpp b/Marlin/src/HAL/shared/backtrace/unwmemaccess.cpp
new file mode 100644
index 0000000000..a4151b38c2
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/unwmemaccess.cpp
@@ -0,0 +1,210 @@
+/***************************************************************************
+ * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
+ * Updated, adapted and several bug fixes on 2018 by Eduardo José Tagle
+ *
+ * This program is PUBLIC DOMAIN.
+ * This means that there is no copyright and anyone is able to take a copy
+ * for free and use it as they wish, with or without modifications, and in
+ * any context, commercially or otherwise. The only limitation is that I
+ * don't guarantee that the software is fit for any purpose or accept any
+ * liability for its use or misuse - this software is without warranty.
+ ***************************************************************************
+ * File Description: Utility functions to access memory
+ **************************************************************************/
+
+#if defined(__arm__) || defined(__thumb__)
+
+#include "unwmemaccess.h"
+#include "../../../inc/MarlinConfig.h"
+
+/* Validate address */
+
+#ifdef ARDUINO_ARCH_SAM
+
+ // For DUE, valid address ranges are
+ // SRAM (0x20070000 - 0x20088000) (96kb)
+ // FLASH (0x00080000 - 0x00100000) (512kb)
+ //
+ #define START_SRAM_ADDR 0x20070000
+ #define END_SRAM_ADDR 0x20088000
+ #define START_FLASH_ADDR 0x00080000
+ #define END_FLASH_ADDR 0x00100000
+
+#elif defined(TARGET_LPC1768)
+
+ // For LPC1769:
+ // SRAM (0x10000000 - 0x10008000) (32kb)
+ // FLASH (0x00000000 - 0x00080000) (512kb)
+ //
+ #define START_SRAM_ADDR 0x10000000
+ #define END_SRAM_ADDR 0x10008000
+ #define START_FLASH_ADDR 0x00000000
+ #define END_FLASH_ADDR 0x00080000
+
+#elif defined(__STM32F1__) || defined(STM32F1xx) || defined(STM32F0xx) || defined(STM32G0xx)
+
+ // For STM32F103ZET6/STM32F103VET6/STM32F0xx
+ // SRAM (0x20000000 - 0x20010000) (64kb)
+ // FLASH (0x08000000 - 0x08080000) (512kb)
+ //
+ #define START_SRAM_ADDR 0x20000000
+ #define END_SRAM_ADDR 0x20010000
+ #define START_FLASH_ADDR 0x08000000
+ #define END_FLASH_ADDR 0x08080000
+
+#elif defined(STM32F4) || defined(STM32F4xx)
+
+ // For STM32F407VET
+ // SRAM (0x20000000 - 0x20030000) (192kb)
+ // FLASH (0x08000000 - 0x08080000) (512kb)
+ //
+ #define START_SRAM_ADDR 0x20000000
+ #define END_SRAM_ADDR 0x20030000
+ #define START_FLASH_ADDR 0x08000000
+ #define END_FLASH_ADDR 0x08080000
+
+#elif MB(REMRAM_V1, NUCLEO_F767ZI)
+
+ // For STM32F765VI in RemRam v1
+ // SRAM (0x20000000 - 0x20080000) (512kb)
+ // FLASH (0x08000000 - 0x08200000) (2048kb)
+ //
+ #define START_SRAM_ADDR 0x20000000
+ #define END_SRAM_ADDR 0x20080000
+ #define START_FLASH_ADDR 0x08000000
+ #define END_FLASH_ADDR 0x08200000
+
+#elif defined(__MK20DX256__)
+
+ // For MK20DX256 in TEENSY 3.1 or TEENSY 3.2
+ // SRAM (0x1FFF8000 - 0x20008000) (64kb)
+ // FLASH (0x00000000 - 0x00040000) (256kb)
+ //
+ #define START_SRAM_ADDR 0x1FFF8000
+ #define END_SRAM_ADDR 0x20008000
+ #define START_FLASH_ADDR 0x00000000
+ #define END_FLASH_ADDR 0x00040000
+
+#elif defined(__MK64FX512__)
+
+ // For MK64FX512 in TEENSY 3.5
+ // SRAM (0x1FFF0000 - 0x20020000) (192kb)
+ // FLASH (0x00000000 - 0x00080000) (512kb)
+ //
+ #define START_SRAM_ADDR 0x1FFF0000
+ #define END_SRAM_ADDR 0x20020000
+ #define START_FLASH_ADDR 0x00000000
+ #define END_FLASH_ADDR 0x00080000
+
+#elif defined(__MK66FX1M0__)
+
+ // For MK66FX1M0 in TEENSY 3.6
+ // SRAM (0x1FFF0000 - 0x20030000) (256kb)
+ // FLASH (0x00000000 - 0x00140000) (1.25Mb)
+ //
+ #define START_SRAM_ADDR 0x1FFF0000
+ #define END_SRAM_ADDR 0x20030000
+ #define START_FLASH_ADDR 0x00000000
+ #define END_FLASH_ADDR 0x00140000
+
+#elif defined(__IMXRT1062__)
+
+ // For IMXRT1062 in TEENSY 4.0/4/1
+ // ITCM (rwx): ORIGIN = 0x00000000, LENGTH = 512K
+ // DTCM (rwx): ORIGIN = 0x20000000, LENGTH = 512K
+ // RAM (rwx): ORIGIN = 0x20200000, LENGTH = 512K
+ // FLASH (rwx): ORIGIN = 0x60000000, LENGTH = 1984K
+ //
+ #define START_SRAM_ADDR 0x00000000
+ #define END_SRAM_ADDR 0x20280000
+ #define START_FLASH_ADDR 0x60000000
+ #define END_FLASH_ADDR 0x601F0000
+
+#elif defined(__SAMD51P20A__)
+
+ // For SAMD51x20, valid address ranges are
+ // SRAM (0x20000000 - 0x20040000) (256kb)
+ // FLASH (0x00000000 - 0x00100000) (1024kb)
+ //
+ #define START_SRAM_ADDR 0x20000000
+ #define END_SRAM_ADDR 0x20040000
+ #define START_FLASH_ADDR 0x00000000
+ #define END_FLASH_ADDR 0x00100000
+
+#else
+ // Generic ARM code, that's testing if an access to the given address would cause a fault or not
+ // It can't guarantee an address is in RAM or Flash only, but we usually don't care
+
+ #define NVIC_FAULT_STAT 0xE000ED28 // Configurable Fault Status Reg.
+ #define NVIC_CFG_CTRL 0xE000ED14 // Configuration Control Register
+ #define NVIC_FAULT_STAT_BFARV 0x00008000 // BFAR is valid
+ #define NVIC_CFG_CTRL_BFHFNMIGN 0x00000100 // Ignore bus fault in NMI/fault
+ #define HW_REG(X) (*((volatile unsigned long *)(X)))
+
+ static bool validate_addr(uint32_t read_address) {
+ bool works = true;
+ uint32_t intDisabled = 0;
+ // Read current interrupt state
+ __asm__ __volatile__ ("MRS %[result], PRIMASK\n\t" : [result]"=r"(intDisabled) :: ); // 0 is int enabled, 1 for disabled
+
+ // Clear bus fault indicator first (write 1 to clear)
+ HW_REG(NVIC_FAULT_STAT) |= NVIC_FAULT_STAT_BFARV;
+ // Ignore bus fault interrupt
+ HW_REG(NVIC_CFG_CTRL) |= NVIC_CFG_CTRL_BFHFNMIGN;
+ // Disable interrupts if not disabled previously
+ if (!intDisabled) __asm__ __volatile__ ("CPSID f");
+ // Probe address
+ *(volatile uint32_t*)read_address;
+ // Check if a fault happened
+ if ((HW_REG(NVIC_FAULT_STAT) & NVIC_FAULT_STAT_BFARV) != 0)
+ works = false;
+ // Enable interrupts again if previously disabled
+ if (!intDisabled) __asm__ __volatile__ ("CPSIE f");
+ // Enable fault interrupt flag
+ HW_REG(NVIC_CFG_CTRL) &= ~NVIC_CFG_CTRL_BFHFNMIGN;
+
+ return works;
+ }
+
+#endif
+
+#ifdef START_SRAM_ADDR
+ static bool validate_addr(uint32_t addr) {
+
+ // Address must be in SRAM range
+ if (addr >= START_SRAM_ADDR && addr < END_SRAM_ADDR)
+ return true;
+
+ // Or in FLASH range
+ if (addr >= START_FLASH_ADDR && addr < END_FLASH_ADDR)
+ return true;
+
+ return false;
+ }
+#endif
+
+bool UnwReadW(const uint32_t a, uint32_t *v) {
+ if (!validate_addr(a))
+ return false;
+
+ *v = *(uint32_t *)a;
+ return true;
+}
+
+bool UnwReadH(const uint32_t a, uint16_t *v) {
+ if (!validate_addr(a))
+ return false;
+
+ *v = *(uint16_t *)a;
+ return true;
+}
+
+bool UnwReadB(const uint32_t a, uint8_t *v) {
+ if (!validate_addr(a))
+ return false;
+
+ *v = *(uint8_t *)a;
+ return true;
+}
+
+#endif // __arm__ || __thumb__
diff --git a/Marlin/src/HAL/shared/backtrace/unwmemaccess.h b/Marlin/src/HAL/shared/backtrace/unwmemaccess.h
new file mode 100644
index 0000000000..b911e343dc
--- /dev/null
+++ b/Marlin/src/HAL/shared/backtrace/unwmemaccess.h
@@ -0,0 +1,22 @@
+/***************************************************************************
+ * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
+ * Updated, adapted and several bug fixes on 2018 by Eduardo José Tagle
+ *
+ * This program is PUBLIC DOMAIN.
+ * This means that there is no copyright and anyone is able to take a copy
+ * for free and use it as they wish, with or without modifications, and in
+ * any context, commercially or otherwise. The only limitation is that I
+ * don't guarantee that the software is fit for any purpose or accept any
+ * liability for its use or misuse - this software is without warranty.
+ ***************************************************************************
+ * File Description: Utility functions to access memory
+ **************************************************************************/
+
+#pragma once
+
+#include "unwarm.h"
+#include
+
+bool UnwReadW(const uint32_t a, uint32_t *v);
+bool UnwReadH(const uint32_t a, uint16_t *v);
+bool UnwReadB(const uint32_t a, uint8_t *v);
diff --git a/Marlin/src/HAL/shared/cpu_exception/exception_arm.cpp b/Marlin/src/HAL/shared/cpu_exception/exception_arm.cpp
new file mode 100644
index 0000000000..e54661c770
--- /dev/null
+++ b/Marlin/src/HAL/shared/cpu_exception/exception_arm.cpp
@@ -0,0 +1,379 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ * Copyright (c) 2020 Cyril Russo
+ *
+ * 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 .
+ *
+ */
+
+/***************************************************************************
+ * ARM CPU Exception handler
+ ***************************************************************************/
+
+#if defined(__arm__) || defined(__thumb__)
+
+
+/*
+ On ARM CPUs exception handling is quite powerful.
+
+ By default, upon a crash, the CPU enters the handlers that have a higher priority than any other interrupts,
+ so, in effect, no (real) interrupt can "interrupt" the handler (it's acting like if interrupts were disabled).
+
+ If the handler is not called as re-entrant (that is, if the crash is not happening inside an interrupt or an handler),
+ then it'll patch the return address to a dumping function (resume_from_fault) and save the crash state.
+ The CPU will exit the handler and, as such, re-allow the other interrupts, and jump to the dumping function.
+ In this function, the usual serial port (USB / HW) will be used to dump the crash (no special configuration required).
+
+ The only case where it requires hardware UART is when it's crashing in an interrupt or a crash handler.
+ In that case, instead of returning to the resume_from_fault function (and thus, re-enabling interrupts),
+ it jumps to this function directly (so with interrupts disabled), after changing the behavior of the serial output
+ wrapper to use the HW uart (and in effect, calling MinSerial::init which triggers a warning if you are using
+ a USB serial port).
+
+ In the case you have a USB serial port, this part will be disabled, and only that part (so that's the reason for
+ the warning).
+ This means that you can't have a crash report if the crash happens in an interrupt or an handler if you are using
+ a USB serial port since it's physically impossible.
+ You will get a crash report in all other cases.
+*/
+
+#include "exception_hook.h"
+#include "../backtrace/backtrace.h"
+#include "../MinSerial.h"
+
+#define HW_REG(X) (*((volatile unsigned long *)(X)))
+
+// Default function use the CPU VTOR register to get the vector table.
+// Accessing the CPU VTOR register is done in assembly since it's the only way that's common to all current tool
+unsigned long get_vtor() { return HW_REG(0xE000ED08); } // Even if it looks like an error, it is not an error
+void * hook_get_hardfault_vector_address(unsigned vtor) { return (void*)(vtor + 0x03); }
+void * hook_get_memfault_vector_address(unsigned vtor) { return (void*)(vtor + 0x04); }
+void * hook_get_busfault_vector_address(unsigned vtor) { return (void*)(vtor + 0x05); }
+void * hook_get_usagefault_vector_address(unsigned vtor) { return (void*)(vtor + 0x06); }
+void * hook_get_reserved_vector_address(unsigned vtor) { return (void*)(vtor + 0x07); }
+
+// Common exception frame for ARM, should work for all ARM CPU
+// Described here (modified for convenience): https://interrupt.memfault.com/blog/cortex-m-fault-debug
+struct __attribute__((packed)) ContextStateFrame {
+ uint32_t r0;
+ uint32_t r1;
+ uint32_t r2;
+ uint32_t r3;
+ uint32_t r12;
+ uint32_t lr;
+ uint32_t pc;
+ uint32_t xpsr;
+};
+
+struct __attribute__((packed)) ContextSavedFrame {
+ uint32_t R0;
+ uint32_t R1;
+ uint32_t R2;
+ uint32_t R3;
+ uint32_t R12;
+ uint32_t LR;
+ uint32_t PC;
+ uint32_t XPSR;
+
+ uint32_t CFSR;
+ uint32_t HFSR;
+ uint32_t DFSR;
+ uint32_t AFSR;
+ uint32_t MMAR;
+ uint32_t BFAR;
+
+ uint32_t ESP;
+ uint32_t ELR;
+};
+
+#if NONE(STM32F0xx, STM32G0xx)
+ extern "C"
+ __attribute__((naked)) void CommonHandler_ASM() {
+ __asm__ __volatile__ (
+ // Bit 2 of LR tells which stack pointer to use (either main or process, only main should be used anyway)
+ "tst lr, #4\n"
+ "ite eq\n"
+ "mrseq r0, msp\n"
+ "mrsne r0, psp\n"
+ // Save the LR in use when being interrupted
+ "mov r1, lr\n"
+ // Get the exception number from the ICSR register
+ "ldr r2, =0xE000ED00\n"
+ "ldr r2, [r2, #4]\n"
+ "b CommonHandler_C\n"
+ );
+ }
+#else // Cortex M0 does not support conditional mov and testing with a constant, so let's have a specific handler for it
+ extern "C"
+ __attribute__((naked)) void CommonHandler_ASM() {
+ __asm__ __volatile__ (
+ ".syntax unified\n"
+ // Save the LR in use when being interrupted
+ "mov r1, lr\n"
+ // Get the exception number from the ICSR register
+ "ldr r2, =0xE000ED00\n"
+ "ldr r2, [r2, #4]\n"
+ "movs r0, #4\n"
+ "tst r1, r0\n"
+ "beq _MSP\n"
+ "mrs r0, psp\n"
+ "b CommonHandler_C\n"
+ "_MSP:\n"
+ "mrs r0, msp\n"
+ "b CommonHandler_C\n"
+ );
+ }
+
+ #if DISABLED(DYNAMIC_VECTORTABLE) // Cortex M0 requires the handler's address to be within 32kB to the actual function to be able to branch to it
+ extern "C" {
+ void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_hardfault();
+ void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_busfault();
+ void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_usagefault();
+ void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_memmanage();
+ void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_nmi();
+ void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception7();
+ void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception8();
+ void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception9();
+ void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception10();
+ void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception13();
+ }
+ //TODO When going off from libmaple, you'll need to replace those by the one from STM32/HAL_MinSerial.cpp
+ #endif
+#endif
+
+// Must be a macro to avoid creating a function frame
+#define HALT_IF_DEBUGGING() \
+ do { \
+ if (HW_REG(0xE000EDF0) & _BV(0)) { \
+ __asm("bkpt 1"); \
+ } \
+} while (0)
+
+// Resume from a fault (if possible) so we can still use interrupt based code for serial output
+// In that case, we will not jump back to the faulty code, but instead to a dumping code and then a
+// basic loop with watchdog calling or manual resetting
+static ContextSavedFrame savedFrame;
+static uint8_t lastCause;
+bool resume_from_fault() {
+ static const char* causestr[] = { "Thread", "Rsvd", "NMI", "Hard", "Mem", "Bus", "Usage", "7", "8", "9", "10", "SVC", "Dbg", "13", "PendSV", "SysTk", "IRQ" };
+ // Reinit the serial link (might only work if implemented in each of your boards)
+ MinSerial::init();
+
+ MinSerial::TX("\n\n## Software Fault detected ##\n");
+ MinSerial::TX("Cause: "); MinSerial::TX(causestr[min(lastCause, (uint8_t)16)]); MinSerial::TX('\n');
+
+ MinSerial::TX("R0 : "); MinSerial::TXHex(savedFrame.R0); MinSerial::TX('\n');
+ MinSerial::TX("R1 : "); MinSerial::TXHex(savedFrame.R1); MinSerial::TX('\n');
+ MinSerial::TX("R2 : "); MinSerial::TXHex(savedFrame.R2); MinSerial::TX('\n');
+ MinSerial::TX("R3 : "); MinSerial::TXHex(savedFrame.R3); MinSerial::TX('\n');
+ MinSerial::TX("R12 : "); MinSerial::TXHex(savedFrame.R12); MinSerial::TX('\n');
+ MinSerial::TX("LR : "); MinSerial::TXHex(savedFrame.LR); MinSerial::TX('\n');
+ MinSerial::TX("PC : "); MinSerial::TXHex(savedFrame.PC); MinSerial::TX('\n');
+ MinSerial::TX("PSR : "); MinSerial::TXHex(savedFrame.XPSR); MinSerial::TX('\n');
+
+ // Configurable Fault Status Register
+ // Consists of MMSR, BFSR and UFSR
+ MinSerial::TX("CFSR : "); MinSerial::TXHex(savedFrame.CFSR); MinSerial::TX('\n');
+
+ // Hard Fault Status Register
+ MinSerial::TX("HFSR : "); MinSerial::TXHex(savedFrame.HFSR); MinSerial::TX('\n');
+
+ // Debug Fault Status Register
+ MinSerial::TX("DFSR : "); MinSerial::TXHex(savedFrame.DFSR); MinSerial::TX('\n');
+
+ // Auxiliary Fault Status Register
+ MinSerial::TX("AFSR : "); MinSerial::TXHex(savedFrame.AFSR); MinSerial::TX('\n');
+
+ // Read the Fault Address Registers. These may not contain valid values.
+ // Check BFARVALID/MMARVALID to see if they are valid values
+ // MemManage Fault Address Register
+ MinSerial::TX("MMAR : "); MinSerial::TXHex(savedFrame.MMAR); MinSerial::TX('\n');
+
+ // Bus Fault Address Register
+ MinSerial::TX("BFAR : "); MinSerial::TXHex(savedFrame.BFAR); MinSerial::TX('\n');
+
+ MinSerial::TX("ExcLR: "); MinSerial::TXHex(savedFrame.ELR); MinSerial::TX('\n');
+ MinSerial::TX("ExcSP: "); MinSerial::TXHex(savedFrame.ESP); MinSerial::TX('\n');
+
+ // The stack pointer is pushed by 8 words upon entering an exception, so we need to revert this
+ backtrace_ex(savedFrame.ESP + 8*4, savedFrame.LR, savedFrame.PC);
+
+ // Call the last resort function here
+ hook_last_resort_func();
+
+ const uint32_t start = millis(), end = start + 100; // 100ms should be enough
+ // We need to wait for the serial buffers to be output but we don't know for how long
+ // So we'll just need to refresh the watchdog for a while and then stop for the system to reboot
+ uint32_t last = start;
+ while (PENDING(last, end)) {
+ hal.watchdog_refresh();
+ while (millis() == last) { /* nada */ }
+ last = millis();
+ MinSerial::TX('.');
+ }
+
+ // Reset now by reinstantiating the bootloader's vector table
+ HW_REG(0xE000ED08) = 0;
+ // Restart watchdog
+ #if DISABLED(USE_WATCHDOG)
+ // No watchdog, let's perform ARMv7 reset instead by writing to AIRCR register with VECTKEY set to SYSRESETREQ
+ HW_REG(0xE000ED0C) = (HW_REG(0xE000ED0C) & 0x0000FFFF) | 0x05FA0004;
+ #endif
+
+ while(1) {} // Bad luck, nothing worked
+}
+
+// Make sure the compiler does not optimize the frame argument away
+extern "C"
+__attribute__((optimize("O0")))
+void CommonHandler_C(ContextStateFrame * frame, unsigned long lr, unsigned long cause) {
+
+ // If you are using it'll stop here
+ HALT_IF_DEBUGGING();
+
+ // Save the state to backtrace later on (don't call memcpy here since the stack can be corrupted)
+ savedFrame.R0 = frame->r0;
+ savedFrame.R1 = frame->r1;
+ savedFrame.R2 = frame->r2;
+ savedFrame.R3 = frame->r3;
+ savedFrame.R12 = frame->r12;
+ savedFrame.LR = frame->lr;
+ savedFrame.PC = frame->pc;
+ savedFrame.XPSR= frame->xpsr;
+ lastCause = cause & 0x1FF;
+
+ volatile uint32_t &CFSR = HW_REG(0xE000ED28);
+ savedFrame.CFSR = CFSR;
+ savedFrame.HFSR = HW_REG(0xE000ED2C);
+ savedFrame.DFSR = HW_REG(0xE000ED30);
+ savedFrame.AFSR = HW_REG(0xE000ED3C);
+ savedFrame.MMAR = HW_REG(0xE000ED34);
+ savedFrame.BFAR = HW_REG(0xE000ED38);
+ savedFrame.ESP = (unsigned long)frame; // Even on return, this should not be overwritten by the CPU
+ savedFrame.ELR = lr;
+
+ // First check if we can resume from this exception to our own handler safely
+ // If we can, then we don't need to disable interrupts and the usual serial code
+ // can be used
+
+ //const uint32_t non_usage_fault_mask = 0x0000FFFF;
+ //const bool non_usage_fault_occurred = (CFSR & non_usage_fault_mask) != 0;
+ // the bottom 8 bits of the xpsr hold the exception number of the
+ // executing exception or 0 if the processor is in Thread mode
+ const bool faulted_from_exception = ((frame->xpsr & 0xFF) != 0);
+ if (!faulted_from_exception) { // Not sure about the non_usage_fault, we want to try anyway, don't we ? && !non_usage_fault_occurred)
+ // Try to resume to our handler here
+ CFSR |= CFSR; // The ARM programmer manual says you must write to 1 all fault bits to clear them so this instruction is correct
+ // The frame will not be valid when returning anymore, let's clean it
+ savedFrame.CFSR = 0;
+
+ frame->pc = (uint32_t)resume_from_fault; // Patch where to return to
+ frame->lr = 0xDEADBEEF; // If our handler returns (it shouldn't), let's make it trigger an exception immediately
+ frame->xpsr = _BV(24); // Need to clean the PSR register to thumb II only
+ MinSerial::force_using_default_output = true;
+ return; // The CPU will resume in our handler hopefully, and we'll try to use default serial output
+ }
+
+ // Sorry, we need to emergency code here since the fault is too dangerous to recover from
+ MinSerial::force_using_default_output = false;
+ resume_from_fault();
+}
+
+void hook_cpu_exceptions() {
+ #if ENABLED(DYNAMIC_VECTORTABLE)
+ // On ARM 32bits CPU, the vector table is like this:
+ // 0x0C => Hardfault
+ // 0x10 => MemFault
+ // 0x14 => BusFault
+ // 0x18 => UsageFault
+
+ // Unfortunately, it's usually run from flash, and we can't write to flash here directly to hook our instruction
+ // We could set an hardware breakpoint, and hook on the fly when it's being called, but this
+ // is hard to get right and would probably break debugger when attached
+
+ // So instead, we'll allocate a new vector table filled with the previous value except
+ // for the fault we are interested in.
+ // Now, comes the issue to figure out what is the current vector table size
+ // There is nothing telling us what is the vector table as it's per-cpu vendor specific.
+ // BUT: we are being called at the end of the setup, so we assume the setup is done
+ // Thus, we can read the current vector table until we find an address that's not in flash, and it would mark the
+ // end of the vector table (skipping the fist entry obviously)
+ // The position of the program in flash is expected to be at 0x08xxx xxxx on all known platform for ARM and the
+ // flash size is available via register 0x1FFFF7E0 on STM32 family, but it's not the case for all ARM boards
+ // (accessing this register might trigger a fault if it's not implemented).
+
+ // So we'll simply mask the top 8 bits of the first handler as an hint of being in the flash or not -that's poor and will
+ // probably break if the flash happens to be more than 128MB, but in this case, we are not magician, we need help from outside.
+
+ unsigned long *vecAddr = (unsigned long*)get_vtor();
+ SERIAL_ECHOPGM("Vector table addr: ");
+ SERIAL_PRINTLN(get_vtor(), PrintBase::Hex);
+
+ #ifdef VECTOR_TABLE_SIZE
+ uint32_t vec_size = VECTOR_TABLE_SIZE;
+ alignas(128) static unsigned long vectable[VECTOR_TABLE_SIZE] ;
+ #else
+ #ifndef IS_IN_FLASH
+ #define IS_IN_FLASH(X) (((unsigned long)X & 0xFF000000) == 0x08000000)
+ #endif
+
+ // When searching for the end of the vector table, this acts as a limit not to overcome
+ #ifndef VECTOR_TABLE_SENTINEL
+ #define VECTOR_TABLE_SENTINEL 80
+ #endif
+
+ // Find the vector table size
+ uint32_t vec_size = 1;
+ while (IS_IN_FLASH(vecAddr[vec_size]) && vec_size < VECTOR_TABLE_SENTINEL)
+ vec_size++;
+
+ // We failed to find a valid vector table size, let's abort hooking up
+ if (vec_size == VECTOR_TABLE_SENTINEL) return;
+ // Poor method that's wasting RAM here, but allocating with malloc and alignment would be worst
+ // 128 bytes alignment is required for writing the VTOR register
+ alignas(128) static unsigned long vectable[VECTOR_TABLE_SENTINEL];
+
+ SERIAL_ECHOPGM("Detected vector table size: ");
+ SERIAL_PRINTLN(vec_size, PrintBase::Hex);
+ #endif
+
+ uint32_t defaultFaultHandler = vecAddr[(unsigned)7];
+ // Copy the current vector table into the new table
+ for (uint32_t i = 0; i < vec_size; i++) {
+ vectable[i] = vecAddr[i];
+ // Replace all default handler by our own handler
+ if (vectable[i] == defaultFaultHandler)
+ vectable[i] = (unsigned long)&CommonHandler_ASM;
+ }
+
+ // Let's hook now with our functions
+ vectable[(unsigned long)hook_get_hardfault_vector_address(0)] = (unsigned long)&CommonHandler_ASM;
+ vectable[(unsigned long)hook_get_memfault_vector_address(0)] = (unsigned long)&CommonHandler_ASM;
+ vectable[(unsigned long)hook_get_busfault_vector_address(0)] = (unsigned long)&CommonHandler_ASM;
+ vectable[(unsigned long)hook_get_usagefault_vector_address(0)] = (unsigned long)&CommonHandler_ASM;
+
+ // Finally swap with our own vector table
+ // This is supposed to be atomic, but let's do that with interrupt disabled
+
+ HW_REG(0xE000ED08) = (unsigned long)vectable | _BV32(29); // 29th bit is for telling the CPU the table is now in SRAM (should be present already)
+
+ SERIAL_ECHOLNPGM("Installed fault handlers");
+ #endif
+}
+
+#endif // __arm__ || __thumb__
diff --git a/Marlin/src/HAL/shared/cpu_exception/exception_hook.cpp b/Marlin/src/HAL/shared/cpu_exception/exception_hook.cpp
new file mode 100644
index 0000000000..93e80f3e85
--- /dev/null
+++ b/Marlin/src/HAL/shared/cpu_exception/exception_hook.cpp
@@ -0,0 +1,28 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 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 "exception_hook.h"
+
+void * __attribute__((weak)) hook_get_hardfault_vector_address(unsigned) { return 0; }
+void * __attribute__((weak)) hook_get_memfault_vector_address(unsigned) { return 0; }
+void * __attribute__((weak)) hook_get_busfault_vector_address(unsigned) { return 0; }
+void * __attribute__((weak)) hook_get_usagefault_vector_address(unsigned) { return 0; }
+void __attribute__((weak)) hook_last_resort_func() {}
diff --git a/Marlin/src/HAL/shared/cpu_exception/exception_hook.h b/Marlin/src/HAL/shared/cpu_exception/exception_hook.h
new file mode 100644
index 0000000000..70d9434704
--- /dev/null
+++ b/Marlin/src/HAL/shared/cpu_exception/exception_hook.h
@@ -0,0 +1,54 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 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
+
+/* Here is the expected behavior of a system producing a CPU exception with this hook installed:
+ 1. Before the system is crashed
+ 1.1 Upon validation (not done yet in this code, but we could be using DEBUG flags here to allow/disallow hooking)
+ 1.2 Install the hook by overwriting the vector table exception handler with the hooked function
+ 2. Upon system crash (for example, by a dereference of a NULL pointer or anything else)
+ 2.1 The CPU triggers its exception and jump into the vector table for the exception type
+ 2.2 Instead of finding the default handler, it finds the updated pointer to our hook
+ 2.3 The CPU jumps into our hook function (likely a naked function to keep all information about crash point intact)
+ 2.4 The hook (naked) function saves the important registers (stack pointer, program counter, current mode) and jumps to a common exception handler (in C)
+ 2.5 The common exception handler dumps the registers on the serial link and perform a backtrace around the crashing point
+ 2.6 Once the backtrace is performed the last resort function is called (platform specific).
+ On some platform with a LCD screen, this might display the crash information as a QR code or as text for the
+ user to capture by taking a picture
+ 2.7 The CPU is reset and/or halted by triggering a debug breakpoint if a debugger is attached */
+
+// Hook into CPU exception interrupt table to call the backtracing code upon an exception
+// Most platform will simply do nothing here, but those who can will install/overwrite the default exception handler
+// with a more performant exception handler
+void hook_cpu_exceptions();
+
+// Some platform might deal without a hard fault handler, in that case, return 0 in your platform here or skip implementing it
+void * __attribute__((weak)) hook_get_hardfault_vector_address(unsigned base_address);
+// Some platform might deal without a memory management fault handler, in that case, return 0 in your platform here or skip implementing it
+void * __attribute__((weak)) hook_get_memfault_vector_address(unsigned base_address);
+// Some platform might deal without a bus fault handler, in that case, return 0 in your platform here or skip implementing it
+void * __attribute__((weak)) hook_get_busfault_vector_address(unsigned base_address);
+// Some platform might deal without a usage fault handler, in that case, return 0 in your platform here or skip implementing it
+void * __attribute__((weak)) hook_get_usagefault_vector_address(unsigned base_address);
+
+// Last resort function that can be called after the exception handler was called.
+void __attribute__((weak)) hook_last_resort_func();
diff --git a/Marlin/src/HAL/shared/eeprom_api.cpp b/Marlin/src/HAL/shared/eeprom_api.cpp
new file mode 100644
index 0000000000..47cfa5a2db
--- /dev/null
+++ b/Marlin/src/HAL/shared/eeprom_api.cpp
@@ -0,0 +1,30 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
+ * Copyright (c) 2016 Victor Perez victor_pv@hotmail.com
+ *
+ * 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/MarlinConfigPre.h"
+
+#if EITHER(EEPROM_SETTINGS, SD_FIRMWARE_UPDATE)
+
+ #include "eeprom_api.h"
+ PersistentStore persistentStore;
+
+#endif
diff --git a/Marlin/src/HAL/shared/eeprom_api.h b/Marlin/src/HAL/shared/eeprom_api.h
new file mode 100644
index 0000000000..cd744f82dc
--- /dev/null
+++ b/Marlin/src/HAL/shared/eeprom_api.h
@@ -0,0 +1,71 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
+ * Copyright (c) 2016 Victor Perez victor_pv@hotmail.com
+ *
+ * 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
+#include
+
+#include "../../libs/crc16.h"
+
+class PersistentStore {
+public:
+
+ // Total available persistent storage space (in bytes)
+ static size_t capacity();
+
+ // Prepare to read or write
+ static bool access_start();
+
+ // Housecleaning after read or write
+ static bool access_finish();
+
+ // Write one or more bytes of data and update the CRC
+ // Return 'true' on write error
+ static bool write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc);
+
+ // Read one or more bytes of data and update the CRC
+ // Return 'true' on read error
+ static bool read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing=true);
+
+ // Write one or more bytes of data
+ // Return 'true' on write error
+ static bool write_data(const int pos, const uint8_t *value, const size_t size=sizeof(uint8_t)) {
+ int data_pos = pos;
+ uint16_t crc = 0;
+ return write_data(data_pos, value, size, &crc);
+ }
+
+ // Write a single byte of data
+ // Return 'true' on write error
+ static bool write_data(const int pos, const uint8_t value) { return write_data(pos, &value); }
+
+ // Read one or more bytes of data
+ // Return 'true' on read error
+ static bool read_data(const int pos, uint8_t *value, const size_t size=1) {
+ int data_pos = pos;
+ uint16_t crc = 0;
+ return read_data(data_pos, value, size, &crc);
+ }
+};
+
+extern PersistentStore persistentStore;
diff --git a/Marlin/src/HAL/shared/eeprom_if.h b/Marlin/src/HAL/shared/eeprom_if.h
new file mode 100644
index 0000000000..e496de2a03
--- /dev/null
+++ b/Marlin/src/HAL/shared/eeprom_if.h
@@ -0,0 +1,29 @@
+/**
+ * Marlin 3D Printer Firmware
+ *
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ * Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
+ *
+ * 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
+
+//
+// EEPROM
+//
+void eeprom_init();
+void eeprom_write_byte(uint8_t *pos, uint8_t value);
+uint8_t eeprom_read_byte(uint8_t *pos);
diff --git a/Marlin/src/HAL/shared/eeprom_if_i2c.cpp b/Marlin/src/HAL/shared/eeprom_if_i2c.cpp
new file mode 100644
index 0000000000..6b559e234b
--- /dev/null
+++ b/Marlin/src/HAL/shared/eeprom_if_i2c.cpp
@@ -0,0 +1,102 @@
+/**
+ * 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 .
+ *
+ */
+
+/**
+ * Platform-independent Arduino functions for I2C EEPROM.
+ * Enable USE_SHARED_EEPROM if not supplied by the framework.
+ */
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(I2C_EEPROM)
+
+#include "eeprom_if.h"
+
+#if ENABLED(SOFT_I2C_EEPROM)
+ #include
+ SlowSoftWire Wire = SlowSoftWire(I2C_SDA_PIN, I2C_SCL_PIN, true);
+#else
+ #include
+#endif
+
+void eeprom_init() {
+ Wire.begin(
+ #if PINS_EXIST(I2C_SCL, I2C_SDA) && DISABLED(SOFT_I2C_EEPROM)
+ uint8_t(I2C_SDA_PIN), uint8_t(I2C_SCL_PIN)
+ #endif
+ );
+}
+
+#if ENABLED(USE_SHARED_EEPROM)
+
+#ifndef EEPROM_WRITE_DELAY
+ #define EEPROM_WRITE_DELAY 5
+#endif
+#ifndef EEPROM_DEVICE_ADDRESS
+ #define EEPROM_DEVICE_ADDRESS 0x50
+#endif
+
+static constexpr uint8_t eeprom_device_address = I2C_ADDRESS(EEPROM_DEVICE_ADDRESS);
+
+// ------------------------
+// Public functions
+// ------------------------
+
+#define SMALL_EEPROM (MARLIN_EEPROM_SIZE <= 2048)
+
+// Combine Address high bits into the device address on <=16Kbit (2K) and >512Kbit (64K) EEPROMs.
+// Note: MARLIN_EEPROM_SIZE is specified in bytes, whereas EEPROM model numbers refer to bits.
+// e.g., The "16" in BL24C16 indicates a 16Kbit (2KB) size.
+static uint8_t _eeprom_calc_device_address(uint8_t * const pos) {
+ const unsigned eeprom_address = (unsigned)pos;
+ return (SMALL_EEPROM || MARLIN_EEPROM_SIZE > 65536)
+ ? uint8_t(eeprom_device_address | ((eeprom_address >> (SMALL_EEPROM ? 8 : 16)) & 0x07))
+ : eeprom_device_address;
+}
+
+static void _eeprom_begin(uint8_t * const pos) {
+ const unsigned eeprom_address = (unsigned)pos;
+ Wire.beginTransmission(_eeprom_calc_device_address(pos));
+ if (!SMALL_EEPROM)
+ Wire.write(uint8_t((eeprom_address >> 8) & 0xFF)); // Address High, if needed
+ Wire.write(uint8_t(eeprom_address & 0xFF)); // Address Low
+}
+
+void eeprom_write_byte(uint8_t *pos, uint8_t value) {
+ _eeprom_begin(pos);
+ Wire.write(value);
+ Wire.endTransmission();
+
+ // wait for write cycle to complete
+ // this could be done more efficiently with "acknowledge polling"
+ delay(EEPROM_WRITE_DELAY);
+}
+
+uint8_t eeprom_read_byte(uint8_t *pos) {
+ _eeprom_begin(pos);
+ Wire.endTransmission();
+ Wire.requestFrom(_eeprom_calc_device_address(pos), (byte)1);
+ return Wire.available() ? Wire.read() : 0xFF;
+}
+
+#endif // USE_SHARED_EEPROM
+#endif // I2C_EEPROM
diff --git a/Marlin/src/HAL/shared/eeprom_if_spi.cpp b/Marlin/src/HAL/shared/eeprom_if_spi.cpp
new file mode 100644
index 0000000000..72c35addcb
--- /dev/null
+++ b/Marlin/src/HAL/shared/eeprom_if_spi.cpp
@@ -0,0 +1,84 @@
+/**
+ * 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 .
+ *
+ */
+
+/**
+ * Platform-independent Arduino functions for SPI EEPROM.
+ * Enable USE_SHARED_EEPROM if not supplied by the framework.
+ */
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(SPI_EEPROM)
+
+#include "eeprom_if.h"
+
+void eeprom_init() {}
+
+#if ENABLED(USE_SHARED_EEPROM)
+
+#define CMD_WREN 6 // WREN
+#define CMD_READ 2 // WRITE
+#define CMD_WRITE 2 // WRITE
+
+#ifndef EEPROM_WRITE_DELAY
+ #define EEPROM_WRITE_DELAY 7
+#endif
+
+static void _eeprom_begin(uint8_t * const pos, const uint8_t cmd) {
+ const uint8_t eeprom_temp[3] = {
+ cmd,
+ (unsigned(pos) >> 8) & 0xFF, // Address High
+ unsigned(pos) & 0xFF // Address Low
+ };
+ WRITE(SPI_EEPROM1_CS_PIN, HIGH); // Usually free already
+ WRITE(SPI_EEPROM1_CS_PIN, LOW); // Activate the Bus
+ spiSend(SPI_CHAN_EEPROM1, eeprom_temp, 3);
+ // Leave the Bus in-use
+}
+
+uint8_t eeprom_read_byte(uint8_t *pos) {
+ _eeprom_begin(pos, CMD_READ); // Set read location and begin transmission
+
+ const uint8_t v = spiRec(SPI_CHAN_EEPROM1); // After READ a value sits on the Bus
+
+ WRITE(SPI_EEPROM1_CS_PIN, HIGH); // Done with device
+
+ return v;
+}
+
+void eeprom_write_byte(uint8_t *pos, uint8_t value) {
+ const uint8_t eeprom_temp = CMD_WREN;
+ WRITE(SPI_EEPROM1_CS_PIN, LOW);
+ spiSend(SPI_CHAN_EEPROM1, &eeprom_temp, 1); // Write Enable
+
+ WRITE(SPI_EEPROM1_CS_PIN, HIGH); // Done with the Bus
+ delay(1); // For a small amount of time
+
+ _eeprom_begin(pos, CMD_WRITE); // Set write address and begin transmission
+
+ spiSend(SPI_CHAN_EEPROM1, value); // Send the value to be written
+ WRITE(SPI_EEPROM1_CS_PIN, HIGH); // Done with the Bus
+ delay(EEPROM_WRITE_DELAY); // Give page write time to complete
+}
+
+#endif // USE_SHARED_EEPROM
+#endif // I2C_EEPROM
diff --git a/Marlin/src/HAL/shared/esp_wifi.cpp b/Marlin/src/HAL/shared/esp_wifi.cpp
new file mode 100644
index 0000000000..a55f5ca39f
--- /dev/null
+++ b/Marlin/src/HAL/shared/esp_wifi.cpp
@@ -0,0 +1,43 @@
+/**
+ * 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 .
+ *
+ */
+
+#include "../../inc/MarlinConfig.h"
+#include "Delay.h"
+
+void esp_wifi_init(void) { // init ESP01 WIFI module pins
+ #if PIN_EXISTS(ESP_WIFI_MODULE_GPIO0)
+ OUT_WRITE(ESP_WIFI_MODULE_GPIO0_PIN, HIGH);
+ #endif
+ #if PIN_EXISTS(ESP_WIFI_MODULE_GPIO2)
+ OUT_WRITE(ESP_WIFI_MODULE_GPIO2_PIN, HIGH);
+ #endif
+ #if PIN_EXISTS(ESP_WIFI_MODULE_RESET)
+ delay(1); // power up delay (0.1mS minimum)
+ OUT_WRITE(ESP_WIFI_MODULE_RESET_PIN, LOW);
+ delay(1);
+ OUT_WRITE(ESP_WIFI_MODULE_RESET_PIN, HIGH);
+ #endif
+ #if PIN_EXISTS(ESP_WIFI_MODULE_ENABLE)
+ delay(1); // delay after reset released (0.1mS minimum)
+ OUT_WRITE(ESP_WIFI_MODULE_ENABLE_PIN, HIGH);
+ #endif
+}
diff --git a/Marlin/src/HAL/shared/esp_wifi.h b/Marlin/src/HAL/shared/esp_wifi.h
new file mode 100644
index 0000000000..84a50a941d
--- /dev/null
+++ b/Marlin/src/HAL/shared/esp_wifi.h
@@ -0,0 +1,24 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+void esp_wifi_init();
diff --git a/Marlin/src/HAL/shared/math_32bit.h b/Marlin/src/HAL/shared/math_32bit.h
new file mode 100644
index 0000000000..1fb233e3e8
--- /dev/null
+++ b/Marlin/src/HAL/shared/math_32bit.h
@@ -0,0 +1,31 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include "../../core/macros.h"
+
+/**
+ * Math helper functions for 32 bit CPUs
+ */
+FORCE_INLINE static uint32_t MultiU32X24toH32(uint32_t longIn1, uint32_t longIn2) {
+ return ((uint64_t)longIn1 * longIn2 + 0x00800000) >> 24;
+}
diff --git a/Marlin/src/HAL/shared/progmem.h b/Marlin/src/HAL/shared/progmem.h
new file mode 100644
index 0000000000..4cd7663df9
--- /dev/null
+++ b/Marlin/src/HAL/shared/progmem.h
@@ -0,0 +1,190 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#ifndef __AVR__
+#ifndef __PGMSPACE_H_
+// This define should prevent reading the system pgmspace.h if included elsewhere
+// This is not normally needed.
+#define __PGMSPACE_H_ 1
+#endif
+
+#ifndef PROGMEM
+#define PROGMEM
+#endif
+#ifndef PGM_P
+#define PGM_P const char *
+#endif
+#ifndef PSTR
+#define PSTR(str) (str)
+#endif
+#ifndef F
+class __FlashStringHelper;
+#define F(str) (reinterpret_cast(PSTR(str)))
+#endif
+#ifndef _SFR_BYTE
+#define _SFR_BYTE(n) (n)
+#endif
+#ifndef memchr_P
+#define memchr_P(str, c, len) memchr((str), (c), (len))
+#endif
+#ifndef memcmp_P
+#define memcmp_P(a, b, n) memcmp((a), (b), (n))
+#endif
+#ifndef memcpy_P
+#define memcpy_P(dest, src, num) memcpy((dest), (src), (num))
+#endif
+#ifndef memmem_P
+#define memmem_P(a, alen, b, blen) memmem((a), (alen), (b), (blen))
+#endif
+#ifndef memrchr_P
+#define memrchr_P(str, val, len) memrchr((str), (val), (len))
+#endif
+#ifndef strcat_P
+#define strcat_P(dest, src) strcat((dest), (src))
+#endif
+#ifndef strchr_P
+#define strchr_P(str, c) strchr((str), (c))
+#endif
+#ifndef strchrnul_P
+#define strchrnul_P(str, c) strchrnul((str), (c))
+#endif
+#ifndef strcmp_P
+#define strcmp_P(a, b) strcmp((a), (b))
+#endif
+#ifndef strcpy_P
+#define strcpy_P(dest, src) strcpy((dest), (src))
+#endif
+#ifndef strcasecmp_P
+#define strcasecmp_P(a, b) strcasecmp((a), (b))
+#endif
+#ifndef strcasestr_P
+#define strcasestr_P(a, b) strcasestr((a), (b))
+#endif
+#ifndef strlcat_P
+#define strlcat_P(dest, src, len) strlcat((dest), (src), (len))
+#endif
+#ifndef strlcpy_P
+#define strlcpy_P(dest, src, len) strlcpy((dest), (src), (len))
+#endif
+#ifndef strlen_P
+#define strlen_P(s) strlen((const char *)(s))
+#endif
+#ifndef strnlen_P
+#define strnlen_P(str, len) strnlen((str), (len))
+#endif
+#ifndef strncmp_P
+#define strncmp_P(a, b, n) strncmp((a), (b), (n))
+#endif
+#ifndef strncasecmp_P
+#define strncasecmp_P(a, b, n) strncasecmp((a), (b), (n))
+#endif
+#ifndef strncat_P
+#define strncat_P(a, b, n) strncat((a), (b), (n))
+#endif
+#ifndef strncpy_P
+#define strncpy_P(a, b, n) strncpy((a), (b), (n))
+#endif
+#ifndef strpbrk_P
+#define strpbrk_P(str, chrs) strpbrk((str), (chrs))
+#endif
+#ifndef strrchr_P
+#define strrchr_P(str, c) strrchr((str), (c))
+#endif
+#ifndef strsep_P
+#define strsep_P(pstr, delim) strsep((pstr), (delim))
+#endif
+#ifndef strspn_P
+#define strspn_P(str, chrs) strspn((str), (chrs))
+#endif
+#ifndef strstr_P
+#define strstr_P(a, b) strstr((a), (b))
+#endif
+#ifndef sprintf_P
+#define sprintf_P(s, ...) sprintf((s), __VA_ARGS__)
+#endif
+#ifndef vfprintf_P
+#define vfprintf_P(s, ...) vfprintf((s), __VA_ARGS__)
+#endif
+#ifndef printf_P
+#define printf_P(...) printf(__VA_ARGS__)
+#endif
+#ifndef snprintf_P
+#define snprintf_P(s, n, ...) snprintf((s), (n), __VA_ARGS__)
+#endif
+#ifndef vsprintf_P
+#define vsprintf_P(s, ...) vsprintf((s),__VA_ARGS__)
+#endif
+#ifndef vsnprintf_P
+#define vsnprintf_P(s, n, ...) vsnprintf((s), (n),__VA_ARGS__)
+#endif
+#ifndef fprintf_P
+#define fprintf_P(s, ...) fprintf((s), __VA_ARGS__)
+#endif
+
+#ifndef pgm_read_byte
+#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
+#endif
+#ifndef pgm_read_word
+#define pgm_read_word(addr) (*(const unsigned short *)(addr))
+#endif
+#ifndef pgm_read_dword
+#define pgm_read_dword(addr) (*(const unsigned long *)(addr))
+#endif
+#ifndef pgm_read_float
+#define pgm_read_float(addr) (*(const float *)(addr))
+#endif
+
+#ifndef pgm_read_byte_near
+#define pgm_read_byte_near(addr) pgm_read_byte(addr)
+#endif
+#ifndef pgm_read_word_near
+#define pgm_read_word_near(addr) pgm_read_word(addr)
+#endif
+#ifndef pgm_read_dword_near
+#define pgm_read_dword_near(addr) pgm_read_dword(addr)
+#endif
+#ifndef pgm_read_float_near
+#define pgm_read_float_near(addr) pgm_read_float(addr)
+#endif
+#ifndef pgm_read_byte_far
+#define pgm_read_byte_far(addr) pgm_read_byte(addr)
+#endif
+#ifndef pgm_read_word_far
+#define pgm_read_word_far(addr) pgm_read_word(addr)
+#endif
+#ifndef pgm_read_dword_far
+#define pgm_read_dword_far(addr) pgm_read_dword(addr)
+#endif
+#ifndef pgm_read_float_far
+#define pgm_read_float_far(addr) pgm_read_float(addr)
+#endif
+
+#ifndef pgm_read_pointer
+#define pgm_read_pointer
+#endif
+
+// Fix bug in pgm_read_ptr
+#undef pgm_read_ptr
+#define pgm_read_ptr(addr) (*((void**)(addr)))
+
+#endif // __AVR__
diff --git a/Marlin/src/HAL/shared/servo.cpp b/Marlin/src/HAL/shared/servo.cpp
new file mode 100644
index 0000000000..b838800de6
--- /dev/null
+++ b/Marlin/src/HAL/shared/servo.cpp
@@ -0,0 +1,157 @@
+/**
+ * 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 .
+ *
+ */
+
+/**
+ * servo.cpp - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
+ * Copyright (c) 2009 Michael Margolis. All right reserved.
+ */
+
+/**
+ * A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method.
+ * The servos are pulsed in the background using the value most recently written using the write() method
+ *
+ * Note that analogWrite of PWM on pins associated with the timer are disabled when the first servo is attached.
+ * Timers are seized as needed in groups of 12 servos - 24 servos use two timers, 48 servos will use four.
+ *
+ * The methods are:
+ *
+ * Servo - Class for manipulating servo motors connected to Arduino pins.
+ *
+ * attach(pin) - Attach a servo motor to an i/o pin.
+ * attach(pin, min, max) - Attach to a pin, setting min and max values in microseconds
+ * Default min is 544, max is 2400
+ *
+ * write() - Set the servo angle in degrees. (Invalid angles —over MIN_PULSE_WIDTH— are treated as µs.)
+ * writeMicroseconds() - Set the servo pulse width in microseconds.
+ * move(pin, angle) - Sequence of attach(pin), write(angle), safe_delay(servo_delay[servoIndex]).
+ * With DEACTIVATE_SERVOS_AFTER_MOVE it detaches after servo_delay[servoIndex].
+ * read() - Get the last-written servo pulse width as an angle between 0 and 180.
+ * readMicroseconds() - Get the last-written servo pulse width in microseconds.
+ * attached() - Return true if a servo is attached.
+ * detach() - Stop an attached servo from pulsing its i/o pin.
+ */
+
+#include "../../inc/MarlinConfig.h"
+
+#if SHARED_SERVOS
+
+#include "servo.h"
+#include "servo_private.h"
+
+ServoInfo_t servo_info[MAX_SERVOS]; // static array of servo info structures
+uint8_t ServoCount = 0; // the total number of attached servos
+
+#define SERVO_MIN(v) (MIN_PULSE_WIDTH - (v) * 4) // minimum value in uS for this servo
+#define SERVO_MAX(v) (MAX_PULSE_WIDTH - (v) * 4) // maximum value in uS for this servo
+
+/************ static functions common to all instances ***********************/
+
+static bool anyTimerChannelActive(const timer16_Sequence_t timer) {
+ // returns true if any servo is active on this timer
+ LOOP_L_N(channel, SERVOS_PER_TIMER) {
+ if (SERVO(timer, channel).Pin.isActive)
+ return true;
+ }
+ return false;
+}
+
+/****************** end of static functions ******************************/
+
+Servo::Servo() {
+ if (ServoCount < MAX_SERVOS) {
+ servoIndex = ServoCount++; // assign a servo index to this instance
+ servo_info[servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values - 12 Aug 2009
+ }
+ else
+ servoIndex = INVALID_SERVO; // too many servos
+}
+
+int8_t Servo::attach(const int inPin) {
+ return attach(inPin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
+}
+
+int8_t Servo::attach(const int inPin, const int inMin, const int inMax) {
+
+ if (servoIndex >= MAX_SERVOS) return -1;
+
+ if (inPin > 0) servo_info[servoIndex].Pin.nbr = inPin;
+ pinMode(servo_info[servoIndex].Pin.nbr, OUTPUT); // set servo pin to output
+
+ // TODO: min/max check: ABS(min - MIN_PULSE_WIDTH) / 4 < 128
+ min = (MIN_PULSE_WIDTH - inMin) / 4; //resolution of min/max is 4 uS
+ max = (MAX_PULSE_WIDTH - inMax) / 4;
+
+ // initialize the timer if it has not already been initialized
+ const timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
+ if (!anyTimerChannelActive(timer)) initISR(timer);
+ servo_info[servoIndex].Pin.isActive = true; // this must be set after the check for anyTimerChannelActive
+
+ return servoIndex;
+}
+
+void Servo::detach() {
+ servo_info[servoIndex].Pin.isActive = false;
+ const timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
+ if (!anyTimerChannelActive(timer)) finISR(timer);
+ //pinMode(servo_info[servoIndex].Pin.nbr, INPUT); // set servo pin to input
+}
+
+void Servo::write(int value) {
+ if (value < MIN_PULSE_WIDTH) // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
+ value = map(constrain(value, 0, 180), 0, 180, SERVO_MIN(min), SERVO_MAX(max));
+ writeMicroseconds(value);
+}
+
+void Servo::writeMicroseconds(int value) {
+ // calculate and store the values for the given channel
+ byte channel = servoIndex;
+ if (channel < MAX_SERVOS) { // ensure channel is valid
+ // ensure pulse width is valid
+ value = constrain(value, SERVO_MIN(min), SERVO_MAX(max)) - (TRIM_DURATION);
+ value = usToTicks(value); // convert to ticks after compensating for interrupt overhead - 12 Aug 2009
+
+ CRITICAL_SECTION_START();
+ servo_info[channel].ticks = value;
+ CRITICAL_SECTION_END();
+ }
+}
+
+// return the value as degrees
+int Servo::read() { return map(readMicroseconds() + 1, SERVO_MIN(min), SERVO_MAX(max), 0, 180); }
+
+int Servo::readMicroseconds() {
+ return (servoIndex == INVALID_SERVO) ? 0 : ticksToUs(servo_info[servoIndex].ticks) + (TRIM_DURATION);
+}
+
+bool Servo::attached() { return servo_info[servoIndex].Pin.isActive; }
+
+void Servo::move(const int value) {
+ constexpr uint16_t servo_delay[] = SERVO_DELAY;
+ static_assert(COUNT(servo_delay) == NUM_SERVOS, "SERVO_DELAY must be an array NUM_SERVOS long.");
+ if (attach(0) >= 0) {
+ write(value);
+ safe_delay(servo_delay[servoIndex]);
+ TERN_(DEACTIVATE_SERVOS_AFTER_MOVE, detach());
+ }
+}
+
+#endif // SHARED_SERVOS
diff --git a/Marlin/src/HAL/shared/servo.h b/Marlin/src/HAL/shared/servo.h
new file mode 100644
index 0000000000..15153ca53f
--- /dev/null
+++ b/Marlin/src/HAL/shared/servo.h
@@ -0,0 +1,115 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+/**
+ * servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
+ * Copyright (c) 2009 Michael Margolis. All right reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method.
+ * The servos are pulsed in the background using the value most recently written using the write() method
+ *
+ * Note that analogWrite of PWM on pins associated with the timer are disabled when the first servo is attached.
+ * Timers are seized as needed in groups of 12 servos - 24 servos use two timers, 48 servos will use four.
+ * The sequence used to seize timers is defined in timers.h
+ *
+ * The methods are:
+ *
+ * Servo - Class for manipulating servo motors connected to Arduino pins.
+ *
+ * attach(pin ) - Attaches a servo motor to an i/o pin.
+ * attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds
+ * default min is 544, max is 2400
+ *
+ * write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds)
+ * writeMicroseconds() - Sets the servo pulse width in microseconds
+ * read() - Gets the last written servo pulse width as an angle between 0 and 180.
+ * readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
+ * attached() - Returns true if there is a servo attached.
+ * detach() - Stops an attached servos from pulsing its i/o pin.
+ * move(angle) - Sequence of attach(0), write(angle),
+ * With DEACTIVATE_SERVOS_AFTER_MOVE wait SERVO_DELAY and detach.
+ */
+
+#if IS_TEENSY32
+ #include "../TEENSY31_32/Servo.h"
+#elif IS_TEENSY35 || IS_TEENSY36
+ #include "../TEENSY35_36/Servo.h"
+#elif IS_TEENSY40 || IS_TEENSY41
+ #include "../TEENSY40_41/Servo.h"
+#elif defined(TARGET_LPC1768)
+ #include "../LPC1768/Servo.h"
+#elif defined(__STM32F1__) || defined(TARGET_STM32F1)
+ #include "../STM32F1/Servo.h"
+#elif defined(ARDUINO_ARCH_STM32)
+ #include "../STM32/Servo.h"
+#elif defined(ARDUINO_ARCH_ESP32)
+ #include "../ESP32/Servo.h"
+#else
+ #include
+
+ #if defined(__AVR__) || defined(ARDUINO_ARCH_SAM) || defined(__SAMD51__) || defined(__SAMD21__)
+ // we're good to go
+ #else
+ #error "This library only supports boards with an AVR, SAM3X, SAMD21 or SAMD51 processor."
+ #endif
+
+ #define Servo_VERSION 2 // software version of this library
+
+ class Servo {
+ public:
+ Servo();
+ int8_t attach(const int pin); // attach the given pin to the next free channel, set pinMode, return channel number (-1 on fail)
+ int8_t attach(const int pin, const int min, const int max); // as above but also sets min and max values for writes.
+ void detach();
+ void write(int value); // if value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
+ void writeMicroseconds(int value); // write pulse width in microseconds
+ void move(const int value); // attach the servo, then move to value
+ // if value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
+ // if DEACTIVATE_SERVOS_AFTER_MOVE wait SERVO_DELAY, then detach
+ int read(); // returns current pulse width as an angle between 0 and 180 degrees
+ int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release)
+ bool attached(); // return true if this servo is attached, otherwise false
+
+ private:
+ uint8_t servoIndex; // index into the channel data for this servo
+ int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH
+ int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH
+ };
+
+#endif
diff --git a/Marlin/src/HAL/shared/servo_private.h b/Marlin/src/HAL/shared/servo_private.h
new file mode 100644
index 0000000000..8fd5ab2d88
--- /dev/null
+++ b/Marlin/src/HAL/shared/servo_private.h
@@ -0,0 +1,100 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+/**
+ * servo_private.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
+ * Copyright (c) 2009 Michael Margolis. All right reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include
+
+// Architecture specific include
+#ifdef __AVR__
+ #include "../AVR/ServoTimers.h"
+#elif defined(ARDUINO_ARCH_SAM)
+ #include "../DUE/ServoTimers.h"
+#elif defined(__SAMD51__)
+ #include "../SAMD51/ServoTimers.h"
+#elif defined(__SAMD21__)
+ #include "../SAMD21/ServoTimers.h"
+#else
+ #error "This library only supports boards with an AVR, SAM3X, SAMD21 or SAMD51 processor."
+#endif
+
+// Macros
+
+#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
+#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
+#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
+#define REFRESH_INTERVAL 20000 // minimum time to refresh servos in microseconds
+
+#define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer
+#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER)
+
+#define INVALID_SERVO 255 // flag indicating an invalid servo index
+
+// Convert microseconds to ticks and back (PRESCALER depends on architecture)
+#define usToTicks(_us) (clockCyclesPerMicrosecond() * (_us) / (SERVO_TIMER_PRESCALER))
+#define ticksToUs(_ticks) (unsigned(_ticks) * (SERVO_TIMER_PRESCALER) / clockCyclesPerMicrosecond())
+
+// convenience macros
+#define SERVO_INDEX_TO_TIMER(_servo_nbr) timer16_Sequence_t(_servo_nbr / (SERVOS_PER_TIMER)) // the timer controlling this servo
+#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % (SERVOS_PER_TIMER)) // the index of the servo on this timer
+#define SERVO_INDEX(_timer,_channel) ((_timer*(SERVOS_PER_TIMER)) + _channel) // servo index by timer and channel
+#define SERVO(_timer,_channel) servo_info[SERVO_INDEX(_timer,_channel)] // servo class by timer and channel
+
+// Types
+
+typedef struct {
+ uint8_t nbr : 7 ; // a pin number from 0 to 127
+ uint8_t isActive : 1 ; // true if this channel is enabled, pin not pulsed if false
+} ServoPin_t;
+
+typedef struct {
+ ServoPin_t Pin;
+ unsigned int ticks;
+} ServoInfo_t;
+
+// Global variables
+
+extern uint8_t ServoCount;
+extern ServoInfo_t servo_info[MAX_SERVOS];
+
+// Public functions
+
+void initISR(const timer16_Sequence_t timer_index);
+void finISR(const timer16_Sequence_t timer_index);
diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp
new file mode 100644
index 0000000000..62f96ddb4b
--- /dev/null
+++ b/Marlin/src/MarlinCore.cpp
@@ -0,0 +1,1690 @@
+/**
+ * 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 .
+ *
+ */
+
+/**
+ * About Marlin
+ *
+ * This firmware is a mashup between Sprinter and grbl.
+ * - https://github.com/kliment/Sprinter
+ * - https://github.com/grbl/grbl
+ */
+
+#include "MarlinCore.h"
+
+#include "HAL/shared/Delay.h"
+#include "HAL/shared/esp_wifi.h"
+#include "HAL/shared/cpu_exception/exception_hook.h"
+
+#ifdef ARDUINO
+ #include
+#endif
+#include
+
+#include "module/endstops.h"
+#include "module/motion.h"
+#include "module/planner.h"
+#include "module/printcounter.h" // PrintCounter or Stopwatch
+#include "module/settings.h"
+#include "module/stepper.h"
+#include "module/temperature.h"
+
+#include "gcode/gcode.h"
+#include "gcode/parser.h"
+#include "gcode/queue.h"
+
+#include "feature/pause.h"
+#include "sd/cardreader.h"
+
+#include "lcd/marlinui.h"
+#if HAS_TOUCH_BUTTONS
+ #include "lcd/touch/touch_buttons.h"
+#endif
+
+#if HAS_TFT_LVGL_UI
+ #include "lcd/extui/mks_ui/tft_lvgl_configuration.h"
+ #include "lcd/extui/mks_ui/draw_ui.h"
+ #include "lcd/extui/mks_ui/mks_hardware.h"
+ #include
+#endif
+
+#if HAS_DWIN_E3V2
+ #include "lcd/e3v2/common/encoder.h"
+ #if ENABLED(DWIN_CREALITY_LCD)
+ #include "lcd/e3v2/creality/dwin.h"
+ #elif ENABLED(DWIN_LCD_PROUI)
+ #include "lcd/e3v2/proui/dwin.h"
+ #endif
+#endif
+
+#if HAS_ETHERNET
+ #include "feature/ethernet.h"
+#endif
+
+#if ENABLED(IIC_BL24CXX_EEPROM)
+ #include "libs/BL24CXX.h"
+#endif
+
+#if ENABLED(DIRECT_STEPPING)
+ #include "feature/direct_stepping.h"
+#endif
+
+#if ENABLED(HOST_ACTION_COMMANDS)
+ #include "feature/host_actions.h"
+#endif
+
+#if HAS_BEEPER
+ #include "libs/buzzer.h"
+#endif
+
+#if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
+ #include "feature/closedloop.h"
+#endif
+
+#if HAS_MOTOR_CURRENT_I2C
+ #include "feature/digipot/digipot.h"
+#endif
+
+#if ENABLED(MIXING_EXTRUDER)
+ #include "feature/mixing.h"
+#endif
+
+#if ENABLED(MAX7219_DEBUG)
+ #include "feature/max7219.h"
+#endif
+
+#if HAS_COLOR_LEDS
+ #include "feature/leds/leds.h"
+#endif
+
+#if ENABLED(BLTOUCH)
+ #include "feature/bltouch.h"
+#endif
+
+#if ENABLED(BD_SENSOR)
+ #include "feature/bedlevel/bdl/bdl.h"
+#endif
+
+#if ENABLED(POLL_JOG)
+ #include "feature/joystick.h"
+#endif
+
+#if HAS_SERVOS
+ #include "module/servo.h"
+#endif
+
+#if HAS_MOTOR_CURRENT_DAC
+ #include "feature/dac/stepper_dac.h"
+#endif
+
+#if ENABLED(EXPERIMENTAL_I2CBUS)
+ #include "feature/twibus.h"
+#endif
+
+#if ENABLED(I2C_POSITION_ENCODERS)
+ #include "feature/encoder_i2c.h"
+#endif
+
+#if (HAS_TRINAMIC_CONFIG || HAS_TMC_SPI) && DISABLED(PSU_DEFAULT_OFF)
+ #include "feature/tmc_util.h"
+#endif
+
+#if HAS_CUTTER
+ #include "feature/spindle_laser.h"
+#endif
+
+#if ENABLED(SDSUPPORT)
+ CardReader card;
+#endif
+
+#if ENABLED(G38_PROBE_TARGET)
+ uint8_t G38_move; // = 0
+ bool G38_did_trigger; // = false
+#endif
+
+#if ENABLED(DELTA)
+ #include "module/delta.h"
+#elif ENABLED(POLARGRAPH)
+ #include "module/polargraph.h"
+#elif IS_SCARA
+ #include "module/scara.h"
+#endif
+
+#if HAS_LEVELING
+ #include "feature/bedlevel/bedlevel.h"
+#endif
+
+#if ENABLED(GCODE_REPEAT_MARKERS)
+ #include "feature/repeat.h"
+#endif
+
+#if ENABLED(POWER_LOSS_RECOVERY)
+ #include "feature/powerloss.h"
+#endif
+
+#if ENABLED(CANCEL_OBJECTS)
+ #include "feature/cancel_object.h"
+#endif
+
+#if HAS_FILAMENT_SENSOR
+ #include "feature/runout.h"
+#endif
+
+#if EITHER(PROBE_TARE, HAS_Z_SERVO_PROBE)
+ #include "module/probe.h"
+#endif
+
+#if ENABLED(HOTEND_IDLE_TIMEOUT)
+ #include "feature/hotend_idle.h"
+#endif
+
+#if ENABLED(TEMP_STAT_LEDS)
+ #include "feature/leds/tempstat.h"
+#endif
+
+#if ENABLED(CASE_LIGHT_ENABLE)
+ #include "feature/caselight.h"
+#endif
+
+#if HAS_FANMUX
+ #include "feature/fanmux.h"
+#endif
+
+#include "module/tool_change.h"
+
+#if HAS_FANCHECK
+ #include "feature/fancheck.h"
+#endif
+
+#if ENABLED(USE_CONTROLLER_FAN)
+ #include "feature/controllerfan.h"
+#endif
+
+#if HAS_PRUSA_MMU1
+ #include "feature/mmu/mmu.h"
+#endif
+
+#if HAS_PRUSA_MMU2
+ #include "feature/mmu/mmu2.h"
+#endif
+
+#if ENABLED(PASSWORD_FEATURE)
+ #include "feature/password/password.h"
+#endif
+
+#if DGUS_LCD_UI_MKS
+ #include "lcd/extui/dgus/DGUSScreenHandler.h"
+#endif
+
+#if HAS_DRIVER_SAFE_POWER_PROTECT
+ #include "feature/stepper_driver_safety.h"
+#endif
+
+#if ENABLED(PSU_CONTROL)
+ #include "feature/power.h"
+#endif
+
+#if ENABLED(EASYTHREED_UI)
+ #include "feature/easythreed_ui.h"
+#endif
+
+#if ENABLED(MARLIN_TEST_BUILD)
+ #include "tests/marlin_tests.h"
+#endif
+
+PGMSTR(M112_KILL_STR, "M112 Shutdown");
+
+MarlinState marlin_state = MF_INITIALIZING;
+
+// For M109 and M190, this flag may be cleared (by M108) to exit the wait loop
+bool wait_for_heatup = true;
+
+// For M0/M1, this flag may be cleared (by M108) to exit the wait-for-user loop
+#if HAS_RESUME_CONTINUE
+ bool wait_for_user; // = false;
+
+ void wait_for_user_response(millis_t ms/*=0*/, const bool no_sleep/*=false*/) {
+ UNUSED(no_sleep);
+ KEEPALIVE_STATE(PAUSED_FOR_USER);
+ wait_for_user = true;
+ if (ms) ms += millis(); // expire time
+ while (wait_for_user && !(ms && ELAPSED(millis(), ms)))
+ idle(TERN_(ADVANCED_PAUSE_FEATURE, no_sleep));
+ wait_for_user = false;
+ while (ui.button_pressed()) safe_delay(50);
+ }
+
+#endif
+
+/**
+ * ***************************************************************************
+ * ******************************** FUNCTIONS ********************************
+ * ***************************************************************************
+ */
+
+/**
+ * Stepper Reset (RigidBoard, et.al.)
+ */
+#if HAS_STEPPER_RESET
+ void disableStepperDrivers() { OUT_WRITE(STEPPER_RESET_PIN, LOW); } // Drive down to keep motor driver chips in reset
+ void enableStepperDrivers() { SET_INPUT(STEPPER_RESET_PIN); } // Set to input, allowing pullups to pull the pin high
+#endif
+
+/**
+ * Sensitive pin test for M42, M226
+ */
+
+#include "pins/sensitive_pins.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnarrowing"
+
+#ifndef RUNTIME_ONLY_ANALOG_TO_DIGITAL
+ template
+ constexpr pin_t OnlyPins<_SP_END, D...>::table[sizeof...(D)];
+#endif
+
+bool pin_is_protected(const pin_t pin) {
+ #ifdef RUNTIME_ONLY_ANALOG_TO_DIGITAL
+ static const pin_t sensitive_pins[] PROGMEM = { SENSITIVE_PINS };
+ const size_t pincount = COUNT(sensitive_pins);
+ #else
+ static constexpr size_t pincount = OnlyPins::size;
+ static const pin_t (&sensitive_pins)[pincount] PROGMEM = OnlyPins::table;
+ #endif
+ LOOP_L_N(i, pincount) {
+ const pin_t * const pptr = &sensitive_pins[i];
+ if (pin == (sizeof(pin_t) == 2 ? (pin_t)pgm_read_word(pptr) : (pin_t)pgm_read_byte(pptr))) return true;
+ }
+ return false;
+}
+
+#pragma GCC diagnostic pop
+
+bool printer_busy() {
+ return planner.movesplanned() || printingIsActive();
+}
+
+/**
+ * A Print Job exists when the timer is running or SD is printing
+ */
+bool printJobOngoing() { return print_job_timer.isRunning() || IS_SD_PRINTING(); }
+
+/**
+ * Printing is active when a job is underway but not paused
+ */
+bool printingIsActive() { return !did_pause_print && printJobOngoing(); }
+
+/**
+ * Printing is paused according to SD or host indicators
+ */
+bool printingIsPaused() {
+ return did_pause_print || print_job_timer.isPaused() || IS_SD_PAUSED();
+}
+
+void startOrResumeJob() {
+ if (!printingIsPaused()) {
+ TERN_(GCODE_REPEAT_MARKERS, repeat.reset());
+ TERN_(CANCEL_OBJECTS, cancelable.reset());
+ TERN_(LCD_SHOW_E_TOTAL, e_move_accumulator = 0);
+ #if ENABLED(SET_REMAINING_TIME)
+ ui.reset_remaining_time();
+ #endif
+ }
+ print_job_timer.start();
+}
+
+#if ENABLED(SDSUPPORT)
+
+ inline void abortSDPrinting() {
+ IF_DISABLED(NO_SD_AUTOSTART, card.autofile_cancel());
+ card.abortFilePrintNow(TERN_(SD_RESORT, true));
+
+ queue.clear();
+ quickstop_stepper();
+
+ print_job_timer.abort();
+
+ IF_DISABLED(SD_ABORT_NO_COOLDOWN, thermalManager.disable_all_heaters());
+
+ TERN(HAS_CUTTER, cutter.kill(), thermalManager.zero_fan_speeds()); // Full cutter shutdown including ISR control
+
+ wait_for_heatup = false;
+
+ TERN_(POWER_LOSS_RECOVERY, recovery.purge());
+
+ #ifdef EVENT_GCODE_SD_ABORT
+ queue.inject(F(EVENT_GCODE_SD_ABORT));
+ #endif
+
+ TERN_(PASSWORD_AFTER_SD_PRINT_ABORT, password.lock_machine());
+ }
+
+ inline void finishSDPrinting() {
+ if (queue.enqueue_one(F("M1001"))) { // Keep trying until it gets queued
+ marlin_state = MF_RUNNING; // Signal to stop trying
+ TERN_(PASSWORD_AFTER_SD_PRINT_END, password.lock_machine());
+ TERN_(DGUS_LCD_UI_MKS, ScreenHandler.SDPrintingFinished());
+ }
+ }
+
+#endif // SDSUPPORT
+
+/**
+ * Minimal management of Marlin's core activities:
+ * - Keep the command buffer full
+ * - Check for maximum inactive time between commands
+ * - Check for maximum inactive time between stepper commands
+ * - Check if CHDK_PIN needs to go LOW
+ * - Check for KILL button held down
+ * - Check for HOME button held down
+ * - Check for CUSTOM USER button held down
+ * - Check if cooling fan needs to be switched on
+ * - Check if an idle but hot extruder needs filament extruded (EXTRUDER_RUNOUT_PREVENT)
+ * - Pulse FET_SAFETY_PIN if it exists
+ */
+inline void manage_inactivity(const bool no_stepper_sleep=false) {
+
+ queue.get_available_commands();
+
+ const millis_t ms = millis();
+
+ // Prevent steppers timing-out
+ const bool do_reset_timeout = no_stepper_sleep
+ || TERN0(PAUSE_PARK_NO_STEPPER_TIMEOUT, did_pause_print);
+
+ // Reset both the M18/M84 activity timeout and the M85 max 'kill' timeout
+ if (do_reset_timeout) gcode.reset_stepper_timeout(ms);
+
+ if (gcode.stepper_max_timed_out(ms)) {
+ SERIAL_ERROR_START();
+ SERIAL_ECHOPGM(STR_KILL_PRE);
+ SERIAL_ECHOLNPGM(STR_KILL_INACTIVE_TIME, parser.command_ptr);
+ kill();
+ }
+
+ const bool has_blocks = planner.has_blocks_queued(); // Any moves in the planner?
+ if (has_blocks) gcode.reset_stepper_timeout(ms); // Reset timeout for M18/M84, M85 max 'kill', and laser.
+
+ // M18 / M84 : Handle steppers inactive time timeout
+ #if HAS_DISABLE_INACTIVE_AXIS
+ if (gcode.stepper_inactive_time) {
+
+ static bool already_shutdown_steppers; // = false
+
+ if (!has_blocks && !do_reset_timeout && gcode.stepper_inactive_timeout()) {
+ if (!already_shutdown_steppers) {
+ already_shutdown_steppers = true;
+
+ // Individual axes will be disabled if configured
+ TERN_(DISABLE_INACTIVE_X, stepper.disable_axis(X_AXIS));
+ TERN_(DISABLE_INACTIVE_Y, stepper.disable_axis(Y_AXIS));
+ TERN_(DISABLE_INACTIVE_Z, stepper.disable_axis(Z_AXIS));
+ TERN_(DISABLE_INACTIVE_I, stepper.disable_axis(I_AXIS));
+ TERN_(DISABLE_INACTIVE_J, stepper.disable_axis(J_AXIS));
+ TERN_(DISABLE_INACTIVE_K, stepper.disable_axis(K_AXIS));
+ TERN_(DISABLE_INACTIVE_U, stepper.disable_axis(U_AXIS));
+ TERN_(DISABLE_INACTIVE_V, stepper.disable_axis(V_AXIS));
+ TERN_(DISABLE_INACTIVE_W, stepper.disable_axis(W_AXIS));
+ TERN_(DISABLE_INACTIVE_EXTRUDER, stepper.disable_e_steppers());
+
+ TERN_(AUTO_BED_LEVELING_UBL, bedlevel.steppers_were_disabled());
+ }
+ }
+ else
+ already_shutdown_steppers = false;
+ }
+ #endif
+
+ #if ENABLED(PHOTO_GCODE) && PIN_EXISTS(CHDK)
+ // Check if CHDK should be set to LOW (after M240 set it HIGH)
+ extern millis_t chdk_timeout;
+ if (chdk_timeout && ELAPSED(ms, chdk_timeout)) {
+ chdk_timeout = 0;
+ WRITE(CHDK_PIN, LOW);
+ }
+ #endif
+
+ #if HAS_KILL
+
+ // Check if the kill button was pressed and wait just in case it was an accidental
+ // key kill key press
+ // -------------------------------------------------------------------------------
+ static int killCount = 0; // make the inactivity button a bit less responsive
+ const int KILL_DELAY = 750;
+ if (kill_state())
+ killCount++;
+ else if (killCount > 0)
+ killCount--;
+
+ // Exceeded threshold and we can confirm that it was not accidental
+ // KILL the machine
+ // ----------------------------------------------------------------
+ if (killCount >= KILL_DELAY) {
+ SERIAL_ERROR_START();
+ SERIAL_ECHOPGM(STR_KILL_PRE);
+ SERIAL_ECHOLNPGM(STR_KILL_BUTTON);
+ kill();
+ }
+ #endif
+
+ #if ENABLED(FREEZE_FEATURE)
+ stepper.frozen = READ(FREEZE_PIN) == FREEZE_STATE;
+ #endif
+
+ #if HAS_HOME
+ // Handle a standalone HOME button
+ constexpr millis_t HOME_DEBOUNCE_DELAY = 1000UL;
+ static millis_t next_home_key_ms; // = 0
+ if (!IS_SD_PRINTING() && !READ(HOME_PIN)) { // HOME_PIN goes LOW when pressed
+ if (ELAPSED(ms, next_home_key_ms)) {
+ next_home_key_ms = ms + HOME_DEBOUNCE_DELAY;
+ LCD_MESSAGE(MSG_AUTO_HOME);
+ queue.inject_P(G28_STR);
+ }
+ }
+ #endif
+
+ #if ENABLED(CUSTOM_USER_BUTTONS)
+ // Handle a custom user button if defined
+ const bool printer_not_busy = !printingIsActive();
+ #define HAS_CUSTOM_USER_BUTTON(N) (PIN_EXISTS(BUTTON##N) && defined(BUTTON##N##_HIT_STATE) && defined(BUTTON##N##_GCODE))
+ #define HAS_BETTER_USER_BUTTON(N) HAS_CUSTOM_USER_BUTTON(N) && defined(BUTTON##N##_DESC)
+ #define _CHECK_CUSTOM_USER_BUTTON(N, CODE) do{ \
+ constexpr millis_t CUB_DEBOUNCE_DELAY_##N = 250UL; \
+ static millis_t next_cub_ms_##N; \
+ if (BUTTON##N##_HIT_STATE == READ(BUTTON##N##_PIN) \
+ && (ENABLED(BUTTON##N##_WHEN_PRINTING) || printer_not_busy)) { \
+ if (ELAPSED(ms, next_cub_ms_##N)) { \
+ next_cub_ms_##N = ms + CUB_DEBOUNCE_DELAY_##N; \
+ CODE; \
+ queue.inject(F(BUTTON##N##_GCODE)); \
+ TERN_(HAS_MARLINUI_MENU, ui.quick_feedback()); \
+ } \
+ } \
+ }while(0)
+
+ #define CHECK_CUSTOM_USER_BUTTON(N) _CHECK_CUSTOM_USER_BUTTON(N, NOOP)
+ #define CHECK_BETTER_USER_BUTTON(N) _CHECK_CUSTOM_USER_BUTTON(N, if (strlen(BUTTON##N##_DESC)) LCD_MESSAGE_F(BUTTON##N##_DESC))
+
+ #if HAS_BETTER_USER_BUTTON(1)
+ CHECK_BETTER_USER_BUTTON(1);
+ #elif HAS_CUSTOM_USER_BUTTON(1)
+ CHECK_CUSTOM_USER_BUTTON(1);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(2)
+ CHECK_BETTER_USER_BUTTON(2);
+ #elif HAS_CUSTOM_USER_BUTTON(2)
+ CHECK_CUSTOM_USER_BUTTON(2);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(3)
+ CHECK_BETTER_USER_BUTTON(3);
+ #elif HAS_CUSTOM_USER_BUTTON(3)
+ CHECK_CUSTOM_USER_BUTTON(3);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(4)
+ CHECK_BETTER_USER_BUTTON(4);
+ #elif HAS_CUSTOM_USER_BUTTON(4)
+ CHECK_CUSTOM_USER_BUTTON(4);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(5)
+ CHECK_BETTER_USER_BUTTON(5);
+ #elif HAS_CUSTOM_USER_BUTTON(5)
+ CHECK_CUSTOM_USER_BUTTON(5);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(6)
+ CHECK_BETTER_USER_BUTTON(6);
+ #elif HAS_CUSTOM_USER_BUTTON(6)
+ CHECK_CUSTOM_USER_BUTTON(6);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(7)
+ CHECK_BETTER_USER_BUTTON(7);
+ #elif HAS_CUSTOM_USER_BUTTON(7)
+ CHECK_CUSTOM_USER_BUTTON(7);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(8)
+ CHECK_BETTER_USER_BUTTON(8);
+ #elif HAS_CUSTOM_USER_BUTTON(8)
+ CHECK_CUSTOM_USER_BUTTON(8);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(9)
+ CHECK_BETTER_USER_BUTTON(9);
+ #elif HAS_CUSTOM_USER_BUTTON(9)
+ CHECK_CUSTOM_USER_BUTTON(9);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(10)
+ CHECK_BETTER_USER_BUTTON(10);
+ #elif HAS_CUSTOM_USER_BUTTON(10)
+ CHECK_CUSTOM_USER_BUTTON(10);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(11)
+ CHECK_BETTER_USER_BUTTON(11);
+ #elif HAS_CUSTOM_USER_BUTTON(11)
+ CHECK_CUSTOM_USER_BUTTON(11);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(12)
+ CHECK_BETTER_USER_BUTTON(12);
+ #elif HAS_CUSTOM_USER_BUTTON(12)
+ CHECK_CUSTOM_USER_BUTTON(12);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(13)
+ CHECK_BETTER_USER_BUTTON(13);
+ #elif HAS_CUSTOM_USER_BUTTON(13)
+ CHECK_CUSTOM_USER_BUTTON(13);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(14)
+ CHECK_BETTER_USER_BUTTON(14);
+ #elif HAS_CUSTOM_USER_BUTTON(14)
+ CHECK_CUSTOM_USER_BUTTON(14);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(15)
+ CHECK_BETTER_USER_BUTTON(15);
+ #elif HAS_CUSTOM_USER_BUTTON(15)
+ CHECK_CUSTOM_USER_BUTTON(15);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(16)
+ CHECK_BETTER_USER_BUTTON(16);
+ #elif HAS_CUSTOM_USER_BUTTON(16)
+ CHECK_CUSTOM_USER_BUTTON(16);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(17)
+ CHECK_BETTER_USER_BUTTON(17);
+ #elif HAS_CUSTOM_USER_BUTTON(17)
+ CHECK_CUSTOM_USER_BUTTON(17);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(18)
+ CHECK_BETTER_USER_BUTTON(18);
+ #elif HAS_CUSTOM_USER_BUTTON(18)
+ CHECK_CUSTOM_USER_BUTTON(18);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(19)
+ CHECK_BETTER_USER_BUTTON(19);
+ #elif HAS_CUSTOM_USER_BUTTON(19)
+ CHECK_CUSTOM_USER_BUTTON(19);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(20)
+ CHECK_BETTER_USER_BUTTON(20);
+ #elif HAS_CUSTOM_USER_BUTTON(20)
+ CHECK_CUSTOM_USER_BUTTON(20);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(21)
+ CHECK_BETTER_USER_BUTTON(21);
+ #elif HAS_CUSTOM_USER_BUTTON(21)
+ CHECK_CUSTOM_USER_BUTTON(21);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(22)
+ CHECK_BETTER_USER_BUTTON(22);
+ #elif HAS_CUSTOM_USER_BUTTON(22)
+ CHECK_CUSTOM_USER_BUTTON(22);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(23)
+ CHECK_BETTER_USER_BUTTON(23);
+ #elif HAS_CUSTOM_USER_BUTTON(23)
+ CHECK_CUSTOM_USER_BUTTON(23);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(24)
+ CHECK_BETTER_USER_BUTTON(24);
+ #elif HAS_CUSTOM_USER_BUTTON(24)
+ CHECK_CUSTOM_USER_BUTTON(24);
+ #endif
+ #if HAS_BETTER_USER_BUTTON(25)
+ CHECK_BETTER_USER_BUTTON(25);
+ #elif HAS_CUSTOM_USER_BUTTON(25)
+ CHECK_CUSTOM_USER_BUTTON(25);
+ #endif
+ #endif
+
+ TERN_(EASYTHREED_UI, easythreed_ui.run());
+
+ TERN_(USE_CONTROLLER_FAN, controllerFan.update()); // Check if fan should be turned on to cool stepper drivers down
+
+ TERN_(AUTO_POWER_CONTROL, powerManager.check(!ui.on_status_screen() || printJobOngoing() || printingIsPaused()));
+
+ TERN_(HOTEND_IDLE_TIMEOUT, hotend_idle.check());
+
+ #if ENABLED(EXTRUDER_RUNOUT_PREVENT)
+ if (thermalManager.degHotend(active_extruder) > (EXTRUDER_RUNOUT_MINTEMP)
+ && ELAPSED(ms, gcode.previous_move_ms + SEC_TO_MS(EXTRUDER_RUNOUT_SECONDS))
+ && !planner.has_blocks_queued()
+ ) {
+ #if HAS_SWITCHING_EXTRUDER
+ bool oldstatus;
+ switch (active_extruder) {
+ default: oldstatus = stepper.AXIS_IS_ENABLED(E_AXIS, 0); stepper.ENABLE_EXTRUDER(0); break;
+ #if E_STEPPERS > 1
+ case 2: case 3: oldstatus = stepper.AXIS_IS_ENABLED(E_AXIS, 1); stepper.ENABLE_EXTRUDER(1); break;
+ #if E_STEPPERS > 2
+ case 4: case 5: oldstatus = stepper.AXIS_IS_ENABLED(E_AXIS, 2); stepper.ENABLE_EXTRUDER(2); break;
+ #if E_STEPPERS > 3
+ case 6: case 7: oldstatus = stepper.AXIS_IS_ENABLED(E_AXIS, 3); stepper.ENABLE_EXTRUDER(3); break;
+ #endif // E_STEPPERS > 3
+ #endif // E_STEPPERS > 2
+ #endif // E_STEPPERS > 1
+ }
+ #else // !HAS_SWITCHING_EXTRUDER
+ bool oldstatus;
+ switch (active_extruder) {
+ default:
+ #define _CASE_EN(N) case N: oldstatus = stepper.AXIS_IS_ENABLED(E_AXIS, N); stepper.ENABLE_EXTRUDER(N); break;
+ REPEAT(E_STEPPERS, _CASE_EN);
+ }
+ #endif
+
+ const float olde = current_position.e;
+ current_position.e += EXTRUDER_RUNOUT_EXTRUDE;
+ line_to_current_position(MMM_TO_MMS(EXTRUDER_RUNOUT_SPEED));
+ current_position.e = olde;
+ planner.set_e_position_mm(olde);
+ planner.synchronize();
+
+ #if HAS_SWITCHING_EXTRUDER
+ switch (active_extruder) {
+ default: if (oldstatus) stepper.ENABLE_EXTRUDER(0); else stepper.DISABLE_EXTRUDER(0); break;
+ #if E_STEPPERS > 1
+ case 2: case 3: if (oldstatus) stepper.ENABLE_EXTRUDER(1); else stepper.DISABLE_EXTRUDER(1); break;
+ #if E_STEPPERS > 2
+ case 4: case 5: if (oldstatus) stepper.ENABLE_EXTRUDER(2); else stepper.DISABLE_EXTRUDER(2); break;
+ #endif // E_STEPPERS > 2
+ #endif // E_STEPPERS > 1
+ }
+ #else // !HAS_SWITCHING_EXTRUDER
+ switch (active_extruder) {
+ #define _CASE_RESTORE(N) case N: if (oldstatus) stepper.ENABLE_EXTRUDER(N); else stepper.DISABLE_EXTRUDER(N); break;
+ REPEAT(E_STEPPERS, _CASE_RESTORE);
+ }
+ #endif // !HAS_SWITCHING_EXTRUDER
+
+ gcode.reset_stepper_timeout(ms);
+ }
+ #endif // EXTRUDER_RUNOUT_PREVENT
+
+ #if ENABLED(DUAL_X_CARRIAGE)
+ // handle delayed move timeout
+ if (delayed_move_time && ELAPSED(ms, delayed_move_time) && IsRunning()) {
+ // travel moves have been received so enact them
+ delayed_move_time = 0xFFFFFFFFUL; // force moves to be done
+ destination = current_position;
+ prepare_line_to_destination();
+ planner.synchronize();
+ }
+ #endif
+
+ TERN_(TEMP_STAT_LEDS, handle_status_leds());
+
+ TERN_(MONITOR_DRIVER_STATUS, monitor_tmc_drivers());
+
+ // Limit check_axes_activity frequency to 10Hz
+ static millis_t next_check_axes_ms = 0;
+ if (ELAPSED(ms, next_check_axes_ms)) {
+ planner.check_axes_activity();
+ next_check_axes_ms = ms + 100UL;
+ }
+
+ #if PIN_EXISTS(FET_SAFETY)
+ static millis_t FET_next;
+ if (ELAPSED(ms, FET_next)) {
+ FET_next = ms + FET_SAFETY_DELAY; // 2µs pulse every FET_SAFETY_DELAY mS
+ OUT_WRITE(FET_SAFETY_PIN, !FET_SAFETY_INVERTED);
+ DELAY_US(2);
+ WRITE(FET_SAFETY_PIN, FET_SAFETY_INVERTED);
+ }
+ #endif
+}
+
+/**
+ * Standard idle routine keeps the machine alive:
+ * - Core Marlin activities
+ * - Manage heaters (and Watchdog)
+ * - Max7219 heartbeat, animation, etc.
+ *
+ * Only after setup() is complete:
+ * - Handle filament runout sensors
+ * - Run HAL idle tasks
+ * - Handle Power-Loss Recovery
+ * - Run StallGuard endstop checks
+ * - Handle SD Card insert / remove
+ * - Handle USB Flash Drive insert / remove
+ * - Announce Host Keepalive state (if any)
+ * - Update the Print Job Timer state
+ * - Update the Beeper queue
+ * - Read Buttons and Update the LCD
+ * - Run i2c Position Encoders
+ * - Auto-report Temperatures / SD Status
+ * - Update the Průša MMU2
+ * - Handle Joystick jogging
+ */
+void idle(bool no_stepper_sleep/*=false*/) {
+ #ifdef MAX7219_DEBUG_PROFILE
+ CodeProfiler idle_profiler;
+ #endif
+
+ #if ENABLED(MARLIN_DEV_MODE)
+ static uint16_t idle_depth = 0;
+ if (++idle_depth > 5) SERIAL_ECHOLNPGM("idle() call depth: ", idle_depth);
+ #endif
+
+ // Bed Distance Sensor task
+ TERN_(BD_SENSOR, bdl.process());
+
+ // Core Marlin activities
+ manage_inactivity(no_stepper_sleep);
+
+ // Manage Heaters (and Watchdog)
+ thermalManager.task();
+
+ // Max7219 heartbeat, animation, etc
+ TERN_(MAX7219_DEBUG, max7219.idle_tasks());
+
+ // Return if setup() isn't completed
+ if (marlin_state == MF_INITIALIZING) goto IDLE_DONE;
+
+ // TODO: Still causing errors
+ (void)check_tool_sensor_stats(active_extruder, true);
+
+ // Handle filament runout sensors
+ #if HAS_FILAMENT_SENSOR
+ if (TERN1(HAS_PRUSA_MMU2, !mmu2.enabled()))
+ runout.run();
+ #endif
+
+ // Run HAL idle tasks
+ hal.idletask();
+
+ // Check network connection
+ TERN_(HAS_ETHERNET, ethernet.check());
+
+ // Handle Power-Loss Recovery
+ #if ENABLED(POWER_LOSS_RECOVERY) && PIN_EXISTS(POWER_LOSS)
+ if (IS_SD_PRINTING()) recovery.outage();
+ #endif
+
+ // Run StallGuard endstop checks
+ #if ENABLED(SPI_ENDSTOPS)
+ if (endstops.tmc_spi_homing.any && TERN1(IMPROVE_HOMING_RELIABILITY, ELAPSED(millis(), sg_guard_period)))
+ LOOP_L_N(i, 4) if (endstops.tmc_spi_homing_check()) break; // Read SGT 4 times per idle loop
+ #endif
+
+ // Handle SD Card insert / remove
+ TERN_(SDSUPPORT, card.manage_media());
+
+ // Handle USB Flash Drive insert / remove
+ TERN_(USB_FLASH_DRIVE_SUPPORT, card.diskIODriver()->idle());
+
+ // Announce Host Keepalive state (if any)
+ TERN_(HOST_KEEPALIVE_FEATURE, gcode.host_keepalive());
+
+ // Update the Print Job Timer state
+ TERN_(PRINTCOUNTER, print_job_timer.tick());
+
+ // Update the Beeper queue
+ TERN_(HAS_BEEPER, buzzer.tick());
+
+ // Handle UI input / draw events
+ TERN(DWIN_CREALITY_LCD, DWIN_Update(), ui.update());
+
+ // Run i2c Position Encoders
+ #if ENABLED(I2C_POSITION_ENCODERS)
+ {
+ static millis_t i2cpem_next_update_ms;
+ if (planner.has_blocks_queued()) {
+ const millis_t ms = millis();
+ if (ELAPSED(ms, i2cpem_next_update_ms)) {
+ I2CPEM.update();
+ i2cpem_next_update_ms = ms + I2CPE_MIN_UPD_TIME_MS;
+ }
+ }
+ }
+ #endif
+
+ // Auto-report Temperatures / SD Status
+ #if HAS_AUTO_REPORTING
+ if (!gcode.autoreport_paused) {
+ TERN_(AUTO_REPORT_TEMPERATURES, thermalManager.auto_reporter.tick());
+ TERN_(AUTO_REPORT_FANS, fan_check.auto_reporter.tick());
+ TERN_(AUTO_REPORT_SD_STATUS, card.auto_reporter.tick());
+ TERN_(AUTO_REPORT_POSITION, position_auto_reporter.tick());
+ TERN_(BUFFER_MONITORING, queue.auto_report_buffer_statistics());
+ }
+ #endif
+
+ // Update the Průša MMU2
+ TERN_(HAS_PRUSA_MMU2, mmu2.mmu_loop());
+
+ // Handle Joystick jogging
+ TERN_(POLL_JOG, joystick.inject_jog_moves());
+
+ // Direct Stepping
+ TERN_(DIRECT_STEPPING, page_manager.write_responses());
+
+ // Update the LVGL interface
+ TERN_(HAS_TFT_LVGL_UI, LV_TASK_HANDLER());
+
+ IDLE_DONE:
+ TERN_(MARLIN_DEV_MODE, idle_depth--);
+ return;
+}
+
+/**
+ * Kill all activity and lock the machine.
+ * After this the machine will need to be reset.
+ */
+void kill(FSTR_P const lcd_error/*=nullptr*/, FSTR_P const lcd_component/*=nullptr*/, const bool steppers_off/*=false*/) {
+ thermalManager.disable_all_heaters();
+
+ TERN_(HAS_CUTTER, cutter.kill()); // Full cutter shutdown including ISR control
+
+ // Echo the LCD message to serial for extra context
+ if (lcd_error) { SERIAL_ECHO_START(); SERIAL_ECHOLNF(lcd_error); }
+
+ #if HAS_DISPLAY
+ ui.kill_screen(lcd_error ?: GET_TEXT_F(MSG_KILLED), lcd_component ?: FPSTR(NUL_STR));
+ #else
+ UNUSED(lcd_error); UNUSED(lcd_component);
+ #endif
+
+ TERN_(HAS_TFT_LVGL_UI, lv_draw_error_message(lcd_error));
+
+ // "Error:Printer halted. kill() called!"
+ SERIAL_ERROR_MSG(STR_ERR_KILLED);
+
+ #ifdef ACTION_ON_KILL
+ hostui.kill();
+ #endif
+
+ minkill(steppers_off);
+}
+
+void minkill(const bool steppers_off/*=false*/) {
+
+ // Wait a short time (allows messages to get out before shutting down.
+ for (int i = 1000; i--;) DELAY_US(600);
+
+ cli(); // Stop interrupts
+
+ // Wait to ensure all interrupts stopped
+ for (int i = 1000; i--;) DELAY_US(250);
+
+ // Reiterate heaters off
+ thermalManager.disable_all_heaters();
+
+ TERN_(HAS_CUTTER, cutter.kill()); // Reiterate cutter shutdown
+
+ // Power off all steppers (for M112) or just the E steppers
+ steppers_off ? stepper.disable_all_steppers() : stepper.disable_e_steppers();
+
+ TERN_(PSU_CONTROL, powerManager.power_off());
+
+ TERN_(HAS_SUICIDE, suicide());
+
+ #if EITHER(HAS_KILL, SOFT_RESET_ON_KILL)
+
+ // Wait for both KILL and ENC to be released
+ while (TERN0(HAS_KILL, kill_state()) || TERN0(SOFT_RESET_ON_KILL, ui.button_pressed()))
+ hal.watchdog_refresh();
+
+ // Wait for either KILL or ENC to be pressed again
+ while (TERN1(HAS_KILL, !kill_state()) && TERN1(SOFT_RESET_ON_KILL, !ui.button_pressed()))
+ hal.watchdog_refresh();
+
+ // Reboot the board
+ hal.reboot();
+
+ #else
+
+ for (;;) hal.watchdog_refresh(); // Wait for RESET button or power-cycle
+
+ #endif
+}
+
+/**
+ * Turn off heaters and stop the print in progress
+ * After a stop the machine may be resumed with M999
+ */
+void stop() {
+ thermalManager.disable_all_heaters(); // 'unpause' taken care of in here
+
+ print_job_timer.stop();
+
+ #if EITHER(PROBING_FANS_OFF, ADVANCED_PAUSE_FANS_PAUSE)
+ thermalManager.set_fans_paused(false); // Un-pause fans for safety
+ #endif
+
+ if (!IsStopped()) {
+ 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;
+ }
+}
+
+inline void tmc_standby_setup() {
+ #if PIN_EXISTS(X_STDBY)
+ SET_INPUT_PULLDOWN(X_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(X2_STDBY)
+ SET_INPUT_PULLDOWN(X2_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(Y_STDBY)
+ SET_INPUT_PULLDOWN(Y_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(Y2_STDBY)
+ SET_INPUT_PULLDOWN(Y2_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(Z_STDBY)
+ SET_INPUT_PULLDOWN(Z_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(Z2_STDBY)
+ SET_INPUT_PULLDOWN(Z2_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(Z3_STDBY)
+ SET_INPUT_PULLDOWN(Z3_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(Z4_STDBY)
+ SET_INPUT_PULLDOWN(Z4_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(I_STDBY)
+ SET_INPUT_PULLDOWN(I_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(J_STDBY)
+ SET_INPUT_PULLDOWN(J_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(K_STDBY)
+ SET_INPUT_PULLDOWN(K_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(U_STDBY)
+ SET_INPUT_PULLDOWN(U_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(V_STDBY)
+ SET_INPUT_PULLDOWN(V_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(W_STDBY)
+ SET_INPUT_PULLDOWN(W_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(E0_STDBY)
+ SET_INPUT_PULLDOWN(E0_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(E1_STDBY)
+ SET_INPUT_PULLDOWN(E1_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(E2_STDBY)
+ SET_INPUT_PULLDOWN(E2_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(E3_STDBY)
+ SET_INPUT_PULLDOWN(E3_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(E4_STDBY)
+ SET_INPUT_PULLDOWN(E4_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(E5_STDBY)
+ SET_INPUT_PULLDOWN(E5_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(E6_STDBY)
+ SET_INPUT_PULLDOWN(E6_STDBY_PIN);
+ #endif
+ #if PIN_EXISTS(E7_STDBY)
+ SET_INPUT_PULLDOWN(E7_STDBY_PIN);
+ #endif
+}
+
+/**
+ * Marlin Firmware entry-point. Abandon Hope All Ye Who Enter Here.
+ * Setup before the program loop:
+ *
+ * - Call any special pre-init set for the board
+ * - Put TMC drivers into Low Power Standby mode
+ * - Init the serial ports (so setup can be debugged)
+ * - Set up the kill and suicide pins
+ * - Prepare (disable) board JTAG and Debug ports
+ * - Init serial for a connected MKS TFT with WiFi
+ * - Install Marlin custom Exception Handlers, if set.
+ * - Init Marlin's HAL interfaces (for SPI, i2c, etc.)
+ * - Init some optional hardware and features:
+ * • MAX Thermocouple pins
+ * • Duet Smart Effector
+ * • Filament Runout Sensor
+ * • TMC220x Stepper Drivers (Serial)
+ * • PSU control
+ * • Power-loss Recovery
+ * • Stepper Driver Reset: DISABLE
+ * • TMC Stepper Drivers (SPI)
+ * • Run hal.init_board() for additional pins setup
+ * • ESP WiFi
+ * - Get the Reset Reason and report it
+ * - Print startup messages and diagnostics
+ * - Calibrate the HAL DELAY for precise timing
+ * - Init the buzzer, possibly a custom timer
+ * - Init more optional hardware:
+ * • Color LED illumination
+ * • Neopixel illumination
+ * • Controller Fan
+ * • Creality DWIN LCD (show boot image)
+ * • Tare the Probe if possible
+ * - Mount the (most likely external) SD Card
+ * - Load settings from EEPROM (or use defaults)
+ * - Init the Ethernet Port
+ * - Init Touch Buttons (for emulated DOGLCD)
+ * - Adjust the (certainly wrong) current position by the home offset
+ * - Init the Planner::position (steps) based on current (native) position
+ * - Initialize more managers and peripherals:
+ * • Temperatures
+ * • Print Job Timer
+ * • Endstops and Endstop Interrupts
+ * • Stepper ISR - Kind of Important!
+ * • Servos
+ * • Servo-based Probe
+ * • Photograph Pin
+ * • Laser/Spindle tool Power / PWM
+ * • Coolant Control
+ * • Bed Probe
+ * • Stepper Driver Reset: ENABLE
+ * • Digipot I2C - Stepper driver current control
+ * • Stepper DAC - Stepper driver current control
+ * • Solenoid (probe, or for other use)
+ * • Home Pin
+ * • Custom User Buttons
+ * • Red/Blue Status LEDs
+ * • Case Light
+ * • Prusa MMU filament changer
+ * • Fan Multiplexer
+ * • Mixing Extruder
+ * • BLTouch Probe
+ * • I2C Position Encoders
+ * • Custom I2C Bus handlers
+ * • Enhanced tools or extruders:
+ * • Switching Extruder
+ * • Switching Nozzle
+ * • Parking Extruder
+ * • Magnetic Parking Extruder
+ * • Switching Toolhead
+ * • Electromagnetic Switching Toolhead
+ * • Watchdog Timer - Also Kind of Important!
+ * • Closed Loop Controller
+ * - Run Startup Commands, if defined
+ * - Tell host to close Host Prompts
+ * - Test Trinamic driver connections
+ * - Init Prusa MMU2 filament changer
+ * - Init and test BL24Cxx EEPROM
+ * - Init Creality DWIN encoder, show faux progress bar
+ * - Reset Status Message / Show Service Messages
+ * - Init MAX7219 LED Matrix
+ * - Init Direct Stepping (Klipper-style motion control)
+ * - Init TFT LVGL UI (with 3D Graphics)
+ * - Apply Password Lock - Hold for Authentication
+ * - Open Touch Screen Calibration screen, if not calibrated
+ * - Set Marlin to RUNNING State
+ */
+void setup() {
+ #ifdef FASTIO_INIT
+ FASTIO_INIT();
+ #endif
+
+ #ifdef BOARD_PREINIT
+ BOARD_PREINIT(); // Low-level init (before serial init)
+ #endif
+
+ tmc_standby_setup(); // TMC Low Power Standby pins must be set early or they're not usable
+
+ // Check startup - does nothing if bootloader sets MCUSR to 0
+ const byte mcu = hal.get_reset_source();
+ hal.clear_reset_source();
+
+ #if ENABLED(MARLIN_DEV_MODE)
+ auto log_current_ms = [&](PGM_P const msg) {
+ SERIAL_ECHO_START();
+ SERIAL_CHAR('['); SERIAL_ECHO(millis()); SERIAL_ECHOPGM("] ");
+ SERIAL_ECHOLNPGM_P(msg);
+ };
+ #define SETUP_LOG(M) log_current_ms(PSTR(M))
+ #else
+ #define SETUP_LOG(...) NOOP
+ #endif
+ #define SETUP_RUN(C) do{ SETUP_LOG(STRINGIFY(C)); C; }while(0)
+
+ MYSERIAL1.begin(BAUDRATE);
+ millis_t serial_connect_timeout = millis() + 1000UL;
+ while (!MYSERIAL1.connected() && PENDING(millis(), serial_connect_timeout)) { /*nada*/ }
+
+ #if HAS_MULTI_SERIAL && !HAS_ETHERNET
+ #ifndef BAUDRATE_2
+ #define BAUDRATE_2 BAUDRATE
+ #endif
+ MYSERIAL2.begin(BAUDRATE_2);
+ serial_connect_timeout = millis() + 1000UL;
+ while (!MYSERIAL2.connected() && PENDING(millis(), serial_connect_timeout)) { /*nada*/ }
+ #ifdef SERIAL_PORT_3
+ #ifndef BAUDRATE_3
+ #define BAUDRATE_3 BAUDRATE
+ #endif
+ MYSERIAL3.begin(BAUDRATE_3);
+ serial_connect_timeout = millis() + 1000UL;
+ while (!MYSERIAL3.connected() && PENDING(millis(), serial_connect_timeout)) { /*nada*/ }
+ #endif
+ #endif
+ SERIAL_ECHOLNPGM("start");
+
+ // Set up these pins early to prevent suicide
+ #if HAS_KILL
+ SETUP_LOG("KILL_PIN");
+ #if KILL_PIN_STATE
+ SET_INPUT_PULLDOWN(KILL_PIN);
+ #else
+ SET_INPUT_PULLUP(KILL_PIN);
+ #endif
+ #endif
+
+ #if ENABLED(FREEZE_FEATURE)
+ SETUP_LOG("FREEZE_PIN");
+ #if FREEZE_STATE
+ SET_INPUT_PULLDOWN(FREEZE_PIN);
+ #else
+ SET_INPUT_PULLUP(FREEZE_PIN);
+ #endif
+ #endif
+
+ #if HAS_SUICIDE
+ SETUP_LOG("SUICIDE_PIN");
+ OUT_WRITE(SUICIDE_PIN, !SUICIDE_PIN_STATE);
+ #endif
+
+ #ifdef JTAGSWD_RESET
+ SETUP_LOG("JTAGSWD_RESET");
+ JTAGSWD_RESET();
+ #endif
+
+ // Disable any hardware debug to free up pins for IO
+ #if ENABLED(DISABLE_DEBUG) && defined(JTAGSWD_DISABLE)
+ delay(10);
+ SETUP_LOG("JTAGSWD_DISABLE");
+ JTAGSWD_DISABLE();
+ #elif ENABLED(DISABLE_JTAG) && defined(JTAG_DISABLE)
+ delay(10);
+ SETUP_LOG("JTAG_DISABLE");
+ JTAG_DISABLE();
+ #endif
+
+ TERN_(DYNAMIC_VECTORTABLE, hook_cpu_exceptions()); // If supported, install Marlin exception handlers at runtime
+
+ SETUP_RUN(hal.init());
+
+ // Init and disable SPI thermocouples; this is still needed
+ #if TEMP_SENSOR_IS_MAX_TC(0) || (TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E0))
+ OUT_WRITE(TEMP_0_CS_PIN, HIGH); // Disable
+ #endif
+ #if TEMP_SENSOR_IS_MAX_TC(1) || (TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E1))
+ OUT_WRITE(TEMP_1_CS_PIN, HIGH);
+ #endif
+
+ #if ENABLED(DUET_SMART_EFFECTOR) && PIN_EXISTS(SMART_EFFECTOR_MOD)
+ OUT_WRITE(SMART_EFFECTOR_MOD_PIN, LOW); // Put Smart Effector into NORMAL mode
+ #endif
+
+ #if HAS_FILAMENT_SENSOR
+ SETUP_RUN(runout.setup());
+ #endif
+
+ #if HAS_TMC220x
+ SETUP_RUN(tmc_serial_begin());
+ #endif
+
+ #if HAS_TMC_SPI
+ #if DISABLED(TMC_USE_SW_SPI)
+ SETUP_RUN(SPI.begin());
+ #endif
+ SETUP_RUN(tmc_init_cs_pins());
+ #endif
+
+ #if ENABLED(PSU_CONTROL)
+ SETUP_LOG("PSU_CONTROL");
+ powerManager.init();
+ #endif
+
+ #if ENABLED(POWER_LOSS_RECOVERY)
+ SETUP_RUN(recovery.setup());
+ #endif
+
+ #if HAS_STEPPER_RESET
+ SETUP_RUN(disableStepperDrivers());
+ #endif
+
+ SETUP_RUN(hal.init_board());
+
+ SETUP_RUN(esp_wifi_init());
+
+ // Report Reset Reason
+ if (mcu & RST_POWER_ON) SERIAL_ECHOLNPGM(STR_POWERUP);
+ if (mcu & RST_EXTERNAL) SERIAL_ECHOLNPGM(STR_EXTERNAL_RESET);
+ if (mcu & RST_BROWN_OUT) SERIAL_ECHOLNPGM(STR_BROWNOUT_RESET);
+ if (mcu & RST_WATCHDOG) SERIAL_ECHOLNPGM(STR_WATCHDOG_RESET);
+ if (mcu & RST_SOFTWARE) SERIAL_ECHOLNPGM(STR_SOFTWARE_RESET);
+
+ #if ProUIex
+ ProEx.C115();
+ #else
+ // Identify myself as Marlin x.x.x
+ SERIAL_ECHOLNPGM("Marlin " SHORT_BUILD_VERSION);
+ #if defined(STRING_DISTRIBUTION_DATE) && defined(STRING_CONFIG_H_AUTHOR)
+ SERIAL_ECHO_MSG(
+ " Last Updated: " STRING_DISTRIBUTION_DATE
+ " | Author: " STRING_CONFIG_H_AUTHOR
+ );
+ #endif
+ SERIAL_ECHO_MSG(" Compiled: " __DATE__);
+ #endif
+ SERIAL_ECHO_MSG(STR_FREE_MEMORY, hal.freeMemory(), STR_PLANNER_BUFFER_BYTES, sizeof(block_t) * (BLOCK_BUFFER_SIZE));
+
+ // Some HAL need precise delay adjustment
+ calibrate_delay_loop();
+
+ // Init buzzer pin(s)
+ #if HAS_BEEPER
+ SETUP_RUN(buzzer.init());
+ #endif
+
+ // Set up LEDs early
+ #if HAS_COLOR_LEDS
+ SETUP_RUN(leds.setup());
+ #endif
+
+ #if ENABLED(NEOPIXEL2_SEPARATE)
+ SETUP_RUN(leds2.setup());
+ #endif
+
+ #if ENABLED(USE_CONTROLLER_FAN) // Set up fan controller to initialize also the default configurations.
+ SETUP_RUN(controllerFan.setup());
+ #endif
+
+ TERN_(HAS_FANCHECK, fan_check.init());
+
+ // UI must be initialized before EEPROM
+ // (because EEPROM code calls the UI).
+
+ SETUP_RUN(ui.init());
+
+ #if PIN_EXISTS(SAFE_POWER)
+ #if HAS_DRIVER_SAFE_POWER_PROTECT
+ SETUP_RUN(stepper_driver_backward_check());
+ #else
+ SETUP_LOG("SAFE_POWER");
+ OUT_WRITE(SAFE_POWER_PIN, HIGH);
+ #endif
+ #endif
+
+ #if BOTH(SDSUPPORT, SDCARD_EEPROM_EMULATION)
+ SETUP_RUN(card.mount()); // Mount media with settings before first_load
+ #endif
+
+ SETUP_RUN(settings.first_load()); // Load data from EEPROM if available (or use defaults)
+ // This also updates variables in the planner, elsewhere
+
+ #if BOTH(HAS_WIRED_LCD, SHOW_BOOTSCREEN)
+ SETUP_RUN(ui.show_bootscreen());
+ const millis_t bootscreen_ms = millis();
+ #endif
+
+ #if ENABLED(PROBE_TARE)
+ SETUP_RUN(probe.tare_init());
+ #endif
+
+ #if HAS_ETHERNET
+ SETUP_RUN(ethernet.init());
+ #endif
+
+ #if HAS_TOUCH_BUTTONS
+ SETUP_RUN(touchBt.init());
+ #endif
+
+ TERN_(HAS_M206_COMMAND, current_position += home_offset); // Init current position based on home_offset
+
+ sync_plan_position(); // Vital to init stepper/planner equivalent for current_position
+
+ SETUP_RUN(thermalManager.init()); // Initialize temperature loop
+
+ SETUP_RUN(print_job_timer.init()); // Initial setup of print job timer
+
+ SETUP_RUN(endstops.init()); // Init endstops and pullups
+
+ #if ENABLED(DELTA) && !HAS_SOFTWARE_ENDSTOPS
+ SETUP_RUN(refresh_delta_clip_start_height()); // Init safe delta height without soft endstops
+ #endif
+
+ SETUP_RUN(stepper.init()); // Init stepper. This enables interrupts!
+
+ #if HAS_SERVOS
+ SETUP_RUN(servo_init());
+ #endif
+
+ #if HAS_Z_SERVO_PROBE
+ SETUP_RUN(probe.servo_probe_init());
+ #endif
+
+ #if HAS_PHOTOGRAPH
+ OUT_WRITE(PHOTOGRAPH_PIN, LOW);
+ #endif
+
+ #if HAS_CUTTER
+ SETUP_RUN(cutter.init());
+ #endif
+
+ #if ENABLED(COOLANT_MIST)
+ OUT_WRITE(COOLANT_MIST_PIN, COOLANT_MIST_INVERT); // Init Mist Coolant OFF
+ #endif
+ #if ENABLED(COOLANT_FLOOD)
+ OUT_WRITE(COOLANT_FLOOD_PIN, COOLANT_FLOOD_INVERT); // Init Flood Coolant OFF
+ #endif
+
+ #if HAS_BED_PROBE
+ #if PIN_EXISTS(PROBE_ENABLE)
+ OUT_WRITE(PROBE_ENABLE_PIN, LOW); // Disable
+ #endif
+ SETUP_RUN(endstops.enable_z_probe(false));
+ #endif
+
+ #if HAS_STEPPER_RESET
+ SETUP_RUN(enableStepperDrivers());
+ #endif
+
+ #if HAS_MOTOR_CURRENT_I2C
+ SETUP_RUN(digipot_i2c.init());
+ #endif
+
+ #if HAS_MOTOR_CURRENT_DAC
+ SETUP_RUN(stepper_dac.init());
+ #endif
+
+ #if EITHER(Z_PROBE_SLED, SOLENOID_PROBE) && HAS_SOLENOID_1
+ OUT_WRITE(SOL1_PIN, LOW); // OFF
+ #endif
+
+ #if HAS_HOME
+ SET_INPUT_PULLUP(HOME_PIN);
+ #endif
+
+ #if ENABLED(CUSTOM_USER_BUTTONS)
+ #define INIT_CUSTOM_USER_BUTTON_PIN(N) do{ SET_INPUT(BUTTON##N##_PIN); WRITE(BUTTON##N##_PIN, !BUTTON##N##_HIT_STATE); }while(0)
+
+ #if HAS_CUSTOM_USER_BUTTON(1)
+ INIT_CUSTOM_USER_BUTTON_PIN(1);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(2)
+ INIT_CUSTOM_USER_BUTTON_PIN(2);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(3)
+ INIT_CUSTOM_USER_BUTTON_PIN(3);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(4)
+ INIT_CUSTOM_USER_BUTTON_PIN(4);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(5)
+ INIT_CUSTOM_USER_BUTTON_PIN(5);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(6)
+ INIT_CUSTOM_USER_BUTTON_PIN(6);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(7)
+ INIT_CUSTOM_USER_BUTTON_PIN(7);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(8)
+ INIT_CUSTOM_USER_BUTTON_PIN(8);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(9)
+ INIT_CUSTOM_USER_BUTTON_PIN(9);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(10)
+ INIT_CUSTOM_USER_BUTTON_PIN(10);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(11)
+ INIT_CUSTOM_USER_BUTTON_PIN(11);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(12)
+ INIT_CUSTOM_USER_BUTTON_PIN(12);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(13)
+ INIT_CUSTOM_USER_BUTTON_PIN(13);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(14)
+ INIT_CUSTOM_USER_BUTTON_PIN(14);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(15)
+ INIT_CUSTOM_USER_BUTTON_PIN(15);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(16)
+ INIT_CUSTOM_USER_BUTTON_PIN(16);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(17)
+ INIT_CUSTOM_USER_BUTTON_PIN(17);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(18)
+ INIT_CUSTOM_USER_BUTTON_PIN(18);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(19)
+ INIT_CUSTOM_USER_BUTTON_PIN(19);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(20)
+ INIT_CUSTOM_USER_BUTTON_PIN(20);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(21)
+ INIT_CUSTOM_USER_BUTTON_PIN(21);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(22)
+ INIT_CUSTOM_USER_BUTTON_PIN(22);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(23)
+ INIT_CUSTOM_USER_BUTTON_PIN(23);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(24)
+ INIT_CUSTOM_USER_BUTTON_PIN(24);
+ #endif
+ #if HAS_CUSTOM_USER_BUTTON(25)
+ INIT_CUSTOM_USER_BUTTON_PIN(25);
+ #endif
+ #endif
+
+ #if PIN_EXISTS(STAT_LED_RED)
+ OUT_WRITE(STAT_LED_RED_PIN, LOW); // OFF
+ #endif
+ #if PIN_EXISTS(STAT_LED_BLUE)
+ OUT_WRITE(STAT_LED_BLUE_PIN, LOW); // OFF
+ #endif
+
+ #if ENABLED(CASE_LIGHT_ENABLE)
+ SETUP_RUN(caselight.init());
+ #endif
+
+ #if HAS_PRUSA_MMU1
+ SETUP_RUN(mmu_init());
+ #endif
+
+ #if HAS_FANMUX
+ SETUP_RUN(fanmux_init());
+ #endif
+
+ #if ENABLED(MIXING_EXTRUDER)
+ SETUP_RUN(mixer.init());
+ #endif
+
+ #if ENABLED(BLTOUCH)
+ SETUP_RUN(bltouch.init(/*set_voltage=*/true));
+ #endif
+
+ #if ENABLED(MAGLEV4)
+ OUT_WRITE(MAGLEV_TRIGGER_PIN, LOW);
+ #endif
+
+ #if ENABLED(I2C_POSITION_ENCODERS)
+ SETUP_RUN(I2CPEM.init());
+ #endif
+
+ #if ENABLED(EXPERIMENTAL_I2CBUS) && I2C_SLAVE_ADDRESS > 0
+ SETUP_LOG("i2c...");
+ i2c.onReceive(i2c_on_receive);
+ i2c.onRequest(i2c_on_request);
+ #endif
+
+ #if DO_SWITCH_EXTRUDER
+ SETUP_RUN(move_extruder_servo(0)); // Initialize extruder servo
+ #endif
+
+ #if ENABLED(SWITCHING_NOZZLE)
+ SETUP_LOG("SWITCHING_NOZZLE");
+ // Initialize nozzle servo(s)
+ #if SWITCHING_NOZZLE_TWO_SERVOS
+ lower_nozzle(0);
+ raise_nozzle(1);
+ #else
+ move_nozzle_servo(0);
+ #endif
+ #endif
+
+ #if ENABLED(PARKING_EXTRUDER)
+ SETUP_RUN(pe_solenoid_init());
+ #elif ENABLED(MAGNETIC_PARKING_EXTRUDER)
+ SETUP_RUN(mpe_settings_init());
+ #elif ENABLED(SWITCHING_TOOLHEAD)
+ SETUP_RUN(swt_init());
+ #elif ENABLED(ELECTROMAGNETIC_SWITCHING_TOOLHEAD)
+ SETUP_RUN(est_init());
+ #endif
+
+ #if ENABLED(USE_WATCHDOG)
+ SETUP_RUN(hal.watchdog_init()); // Reinit watchdog after hal.get_reset_source call
+ #endif
+
+ #if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
+ SETUP_RUN(closedloop.init());
+ #endif
+
+ #ifdef STARTUP_COMMANDS
+ SETUP_LOG("STARTUP_COMMANDS");
+ queue.inject(F(STARTUP_COMMANDS));
+ #endif
+
+ #if ENABLED(HOST_PROMPT_SUPPORT)
+ SETUP_RUN(hostui.prompt_end());
+ #endif
+
+ #if HAS_DRIVER_SAFE_POWER_PROTECT
+ SETUP_RUN(stepper_driver_backward_report());
+ #endif
+
+ #if HAS_PRUSA_MMU2
+ SETUP_RUN(mmu2.init());
+ #endif
+
+ #if ENABLED(IIC_BL24CXX_EEPROM)
+ BL24CXX::init();
+ const uint8_t err = BL24CXX::check();
+ SERIAL_ECHO_TERNARY(err, "BL24CXX Check ", "failed", "succeeded", "!\n");
+ #endif
+
+ #if HAS_DWIN_E3V2_BASIC
+ SETUP_RUN(DWIN_InitScreen());
+ #endif
+
+ #if HAS_SERVICE_INTERVALS && !HAS_DWIN_E3V2_BASIC
+ SETUP_RUN(ui.reset_status(true)); // Show service messages or keep current status
+ #endif
+
+ #if ENABLED(MAX7219_DEBUG)
+ SETUP_RUN(max7219.init());
+ #endif
+
+ #if ENABLED(DIRECT_STEPPING)
+ SETUP_RUN(page_manager.init());
+ #endif
+
+ #if HAS_TFT_LVGL_UI
+ #if ENABLED(SDSUPPORT)
+ if (!card.isMounted()) SETUP_RUN(card.mount()); // Mount SD to load graphics and fonts
+ #endif
+ SETUP_RUN(tft_lvgl_init());
+ #endif
+
+ #if BOTH(HAS_WIRED_LCD, SHOW_BOOTSCREEN)
+ const millis_t elapsed = millis() - bootscreen_ms;
+ #if ENABLED(MARLIN_DEV_MODE)
+ SERIAL_ECHOLNPGM("elapsed=", elapsed);
+ #endif
+ SETUP_RUN(ui.bootscreen_completion(elapsed));
+ #endif
+
+ #if ENABLED(PASSWORD_ON_STARTUP)
+ SETUP_RUN(password.lock_machine()); // Will not proceed until correct password provided
+ #endif
+
+ #if BOTH(HAS_MARLINUI_MENU, TOUCH_SCREEN_CALIBRATION) && EITHER(TFT_CLASSIC_UI, TFT_COLOR_UI)
+ SETUP_RUN(ui.check_touch_calibration());
+ #endif
+
+ #if ENABLED(EASYTHREED_UI)
+ SETUP_RUN(easythreed_ui.init());
+ #endif
+
+ #if HAS_TRINAMIC_CONFIG && DISABLED(PSU_DEFAULT_OFF)
+ SETUP_RUN(test_tmc_connection());
+ #endif
+
+ #if ENABLED(BD_SENSOR)
+ SETUP_RUN(bdl.init(I2C_BD_SDA_PIN, I2C_BD_SCL_PIN, I2C_BD_DELAY));
+ #endif
+
+ marlin_state = MF_RUNNING;
+
+ SETUP_LOG("setup() completed.");
+
+ TERN_(MARLIN_TEST_BUILD, runStartupTests());
+}
+
+/**
+ * The main Marlin program loop
+ *
+ * - Call idle() to handle all tasks between G-code commands
+ * Note that no G-codes from the queue can be executed during idle()
+ * but many G-codes can be called directly anytime like macros.
+ * - Check whether SD card auto-start is needed now.
+ * - Check whether SD print finishing is needed now.
+ * - Run one G-code command from the immediate or main command queue
+ * and open up one space. Commands in the main queue may come from sd
+ * card, host, or by direct injection. The queue will continue to fill
+ * as long as idle() or manage_inactivity() are being called.
+ */
+void loop() {
+ do {
+ idle();
+
+ #if ENABLED(SDSUPPORT)
+ if (card.flag.abort_sd_printing) abortSDPrinting();
+ if (marlin_state == MF_SD_COMPLETE) finishSDPrinting();
+ #endif
+
+ queue.advance();
+
+ #if EITHER(POWER_OFF_TIMER, POWER_OFF_WAIT_FOR_COOLDOWN)
+ powerManager.checkAutoPowerOff();
+ #endif
+
+ endstops.event_handler();
+
+ TERN_(HAS_TFT_LVGL_UI, printer_state_polling());
+
+ TERN_(MARLIN_TEST_BUILD, runPeriodicTests());
+
+ } while (ENABLED(__AVR__)); // Loop forever on slower (AVR) boards
+}
diff --git a/Marlin/src/MarlinCore.h b/Marlin/src/MarlinCore.h
new file mode 100644
index 0000000000..f80405a302
--- /dev/null
+++ b/Marlin/src/MarlinCore.h
@@ -0,0 +1,86 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include "inc/MarlinConfig.h"
+
+#include
+#include
+#include
+
+void stop();
+
+// Pass true to keep steppers from timing out
+void idle(bool no_stepper_sleep=false);
+inline void idle_no_sleep() { idle(true); }
+
+#if ENABLED(G38_PROBE_TARGET)
+ extern uint8_t G38_move; // Flag to tell the ISR that G38 is in progress, and the type
+ extern bool G38_did_trigger; // Flag from the ISR to indicate the endstop changed
+#endif
+
+void kill(FSTR_P const lcd_error=nullptr, FSTR_P const lcd_component=nullptr, const bool steppers_off=false);
+void minkill(const bool steppers_off=false);
+
+// Global State of the firmware
+enum MarlinState : uint8_t {
+ MF_INITIALIZING = 0,
+ MF_STOPPED,
+ MF_KILLED,
+ MF_RUNNING,
+ MF_SD_COMPLETE,
+ MF_PAUSED,
+ MF_WAITING,
+};
+
+extern MarlinState marlin_state;
+inline bool IsRunning() { return marlin_state >= MF_RUNNING; }
+inline bool IsStopped() { return marlin_state == MF_STOPPED; }
+
+bool printingIsActive();
+bool printJobOngoing();
+bool printingIsPaused();
+void startOrResumeJob();
+
+bool printer_busy();
+
+extern bool wait_for_heatup;
+
+#if HAS_RESUME_CONTINUE
+ extern bool wait_for_user;
+ void wait_for_user_response(millis_t ms=0, const bool no_sleep=false);
+#endif
+
+bool pin_is_protected(const pin_t pin);
+
+#if HAS_SUICIDE
+ inline void suicide() { OUT_WRITE(SUICIDE_PIN, SUICIDE_PIN_STATE); }
+#endif
+
+#if HAS_KILL
+ #ifndef KILL_PIN_STATE
+ #define KILL_PIN_STATE LOW
+ #endif
+ inline bool kill_state() { return READ(KILL_PIN) == KILL_PIN_STATE; }
+#endif
+
+extern const char M112_KILL_STR[];
diff --git a/Marlin/src/core/boards.h b/Marlin/src/core/boards.h
new file mode 100644
index 0000000000..d98a1a1483
--- /dev/null
+++ b/Marlin/src/core/boards.h
@@ -0,0 +1,489 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include "macros.h"
+
+#define BOARD_UNKNOWN -1
+
+//
+// RAMPS 1.3 / 1.4 - ATmega1280, ATmega2560
+//
+
+#define BOARD_RAMPS_OLD 1000 // MEGA/RAMPS up to 1.2
+
+#define BOARD_RAMPS_13_EFB 1010 // RAMPS 1.3 (Power outputs: Hotend, Fan, Bed)
+#define BOARD_RAMPS_13_EEB 1011 // RAMPS 1.3 (Power outputs: Hotend0, Hotend1, Bed)
+#define BOARD_RAMPS_13_EFF 1012 // RAMPS 1.3 (Power outputs: Hotend, Fan0, Fan1)
+#define BOARD_RAMPS_13_EEF 1013 // RAMPS 1.3 (Power outputs: Hotend0, Hotend1, Fan)
+#define BOARD_RAMPS_13_SF 1014 // RAMPS 1.3 (Power outputs: Spindle, Controller Fan)
+
+#define BOARD_RAMPS_14_EFB 1020 // RAMPS 1.4 (Power outputs: Hotend, Fan, Bed)
+#define BOARD_RAMPS_14_EEB 1021 // RAMPS 1.4 (Power outputs: Hotend0, Hotend1, Bed)
+#define BOARD_RAMPS_14_EFF 1022 // RAMPS 1.4 (Power outputs: Hotend, Fan0, Fan1)
+#define BOARD_RAMPS_14_EEF 1023 // RAMPS 1.4 (Power outputs: Hotend0, Hotend1, Fan)
+#define BOARD_RAMPS_14_SF 1024 // RAMPS 1.4 (Power outputs: Spindle, Controller Fan)
+
+#define BOARD_RAMPS_PLUS_EFB 1030 // RAMPS Plus 3DYMY (Power outputs: Hotend, Fan, Bed)
+#define BOARD_RAMPS_PLUS_EEB 1031 // RAMPS Plus 3DYMY (Power outputs: Hotend0, Hotend1, Bed)
+#define BOARD_RAMPS_PLUS_EFF 1032 // RAMPS Plus 3DYMY (Power outputs: Hotend, Fan0, Fan1)
+#define BOARD_RAMPS_PLUS_EEF 1033 // RAMPS Plus 3DYMY (Power outputs: Hotend0, Hotend1, Fan)
+#define BOARD_RAMPS_PLUS_SF 1034 // RAMPS Plus 3DYMY (Power outputs: Spindle, Controller Fan)
+
+//
+// RAMPS Derivatives - ATmega1280, ATmega2560
+//
+
+#define BOARD_3DRAG 1100 // 3Drag Controller
+#define BOARD_K8200 1101 // Velleman K8200 Controller (derived from 3Drag Controller)
+#define BOARD_K8400 1102 // Velleman K8400 Controller (derived from 3Drag Controller)
+#define BOARD_K8600 1103 // Velleman K8600 Controller (Vertex Nano)
+#define BOARD_K8800 1104 // Velleman K8800 Controller (Vertex Delta)
+#define BOARD_BAM_DICE 1105 // 2PrintBeta BAM&DICE with STK drivers
+#define BOARD_BAM_DICE_DUE 1106 // 2PrintBeta BAM&DICE Due with STK drivers
+#define BOARD_MKS_BASE 1107 // MKS BASE v1.0
+#define BOARD_MKS_BASE_14 1108 // MKS BASE v1.4 with Allegro A4982 stepper drivers
+#define BOARD_MKS_BASE_15 1109 // MKS BASE v1.5 with Allegro A4982 stepper drivers
+#define BOARD_MKS_BASE_16 1110 // MKS BASE v1.6 with Allegro A4982 stepper drivers
+#define BOARD_MKS_BASE_HEROIC 1111 // MKS BASE 1.0 with Heroic HR4982 stepper drivers
+#define BOARD_MKS_GEN_13 1112 // MKS GEN v1.3 or 1.4
+#define BOARD_MKS_GEN_L 1113 // MKS GEN L
+#define BOARD_KFB_2 1114 // BigTreeTech or BIQU KFB2.0
+#define BOARD_ZRIB_V20 1115 // zrib V2.0 (Chinese RAMPS replica)
+#define BOARD_ZRIB_V52 1116 // zrib V5.2 (Chinese RAMPS replica)
+#define BOARD_FELIX2 1117 // Felix 2.0+ Electronics Board (RAMPS like)
+#define BOARD_RIGIDBOARD 1118 // Invent-A-Part RigidBoard
+#define BOARD_RIGIDBOARD_V2 1119 // Invent-A-Part RigidBoard V2
+#define BOARD_SAINSMART_2IN1 1120 // Sainsmart 2-in-1 board
+#define BOARD_ULTIMAKER 1121 // Ultimaker
+#define BOARD_ULTIMAKER_OLD 1122 // Ultimaker (Older electronics. Pre 1.5.4. This is rare)
+#define BOARD_AZTEEG_X3 1123 // Azteeg X3
+#define BOARD_AZTEEG_X3_PRO 1124 // Azteeg X3 Pro
+#define BOARD_ULTIMAIN_2 1125 // Ultimainboard 2.x (Uses TEMP_SENSOR 20)
+#define BOARD_RUMBA 1126 // Rumba
+#define BOARD_RUMBA_RAISE3D 1127 // Raise3D N series Rumba derivative
+#define BOARD_RL200 1128 // Rapide Lite 200 (v1, low-cost RUMBA clone with drv)
+#define BOARD_FORMBOT_TREX2PLUS 1129 // Formbot T-Rex 2 Plus
+#define BOARD_FORMBOT_TREX3 1130 // Formbot T-Rex 3
+#define BOARD_FORMBOT_RAPTOR 1131 // Formbot Raptor
+#define BOARD_FORMBOT_RAPTOR2 1132 // Formbot Raptor 2
+#define BOARD_BQ_ZUM_MEGA_3D 1133 // bq ZUM Mega 3D
+#define BOARD_MAKEBOARD_MINI 1134 // MakeBoard Mini v2.1.2 by MicroMake
+#define BOARD_TRIGORILLA_13 1135 // TriGorilla Anycubic version 1.3-based on RAMPS EFB
+#define BOARD_TRIGORILLA_14 1136 // ... Ver 1.4
+#define BOARD_TRIGORILLA_14_11 1137 // ... Rev 1.1 (new servo pin order)
+#define BOARD_RAMPS_ENDER_4 1138 // Creality: Ender-4, CR-8
+#define BOARD_RAMPS_CREALITY 1139 // Creality: CR10S, CR20, CR-X
+#define BOARD_DAGOMA_F5 1140 // Dagoma F5
+#define BOARD_FYSETC_F6_13 1141 // FYSETC F6 1.3
+#define BOARD_FYSETC_F6_14 1142 // FYSETC F6 1.4
+#define BOARD_DUPLICATOR_I3_PLUS 1143 // Wanhao Duplicator i3 Plus
+#define BOARD_VORON 1144 // VORON Design
+#define BOARD_TRONXY_V3_1_0 1145 // Tronxy TRONXY-V3-1.0
+#define BOARD_Z_BOLT_X_SERIES 1146 // Z-Bolt X Series
+#define BOARD_TT_OSCAR 1147 // TT OSCAR
+#define BOARD_OVERLORD 1148 // Overlord/Overlord Pro
+#define BOARD_HJC2560C_REV1 1149 // ADIMLab Gantry v1
+#define BOARD_HJC2560C_REV2 1150 // ADIMLab Gantry v2
+#define BOARD_TANGO 1151 // BIQU Tango V1
+#define BOARD_MKS_GEN_L_V2 1152 // MKS GEN L V2
+#define BOARD_MKS_GEN_L_V21 1153 // MKS GEN L V2.1
+#define BOARD_COPYMASTER_3D 1154 // Copymaster 3D
+#define BOARD_ORTUR_4 1155 // Ortur 4
+#define BOARD_TENLOG_D3_HERO 1156 // Tenlog D3 Hero IDEX printer
+#define BOARD_TENLOG_MB1_V23 1157 // Tenlog D3, D5, D6 IDEX Printer
+#define BOARD_RAMPS_S_12_EEFB 1158 // Ramps S 1.2 by Sakul.cz (Power outputs: Hotend0, Hotend1, Fan, Bed)
+#define BOARD_RAMPS_S_12_EEEB 1159 // Ramps S 1.2 by Sakul.cz (Power outputs: Hotend0, Hotend1, Hotend2, Bed)
+#define BOARD_RAMPS_S_12_EFFB 1160 // Ramps S 1.2 by Sakul.cz (Power outputs: Hotend, Fan0, Fan1, Bed)
+#define BOARD_LONGER3D_LK1_PRO 1161 // Longer LK1 PRO / Alfawise U20 Pro (PRO version)
+#define BOARD_LONGER3D_LKx_PRO 1162 // Longer LKx PRO / Alfawise Uxx Pro (PRO version)
+#define BOARD_ZRIB_V53 1163 // Zonestar zrib V5.3 (Chinese RAMPS replica)
+#define BOARD_PXMALION_CORE_I3 1164 // Pxmalion Core I3
+
+//
+// RAMBo and derivatives
+//
+
+#define BOARD_RAMBO 1200 // Rambo
+#define BOARD_MINIRAMBO 1201 // Mini-Rambo
+#define BOARD_MINIRAMBO_10A 1202 // Mini-Rambo 1.0a
+#define BOARD_EINSY_RAMBO 1203 // Einsy Rambo
+#define BOARD_EINSY_RETRO 1204 // Einsy Retro
+#define BOARD_SCOOVO_X9H 1205 // abee Scoovo X9H
+#define BOARD_RAMBO_THINKERV2 1206 // ThinkerV2
+
+//
+// Other ATmega1280, ATmega2560
+//
+
+#define BOARD_CNCONTROLS_11 1300 // Cartesio CN Controls V11
+#define BOARD_CNCONTROLS_12 1301 // Cartesio CN Controls V12
+#define BOARD_CNCONTROLS_15 1302 // Cartesio CN Controls V15
+#define BOARD_CHEAPTRONIC 1303 // Cheaptronic v1.0
+#define BOARD_CHEAPTRONIC_V2 1304 // Cheaptronic v2.0
+#define BOARD_MIGHTYBOARD_REVE 1305 // Makerbot Mightyboard Revision E
+#define BOARD_MEGATRONICS 1306 // Megatronics
+#define BOARD_MEGATRONICS_2 1307 // Megatronics v2.0
+#define BOARD_MEGATRONICS_3 1308 // Megatronics v3.0
+#define BOARD_MEGATRONICS_31 1309 // Megatronics v3.1
+#define BOARD_MEGATRONICS_32 1310 // Megatronics v3.2
+#define BOARD_ELEFU_3 1311 // Elefu Ra Board (v3)
+#define BOARD_LEAPFROG 1312 // Leapfrog
+#define BOARD_MEGACONTROLLER 1313 // Mega controller
+#define BOARD_GT2560_REV_A 1314 // Geeetech GT2560 Rev A
+#define BOARD_GT2560_REV_A_PLUS 1315 // Geeetech GT2560 Rev A+ (with auto level probe)
+#define BOARD_GT2560_REV_B 1316 // Geeetech GT2560 Rev B
+#define BOARD_GT2560_V3 1317 // Geeetech GT2560 Rev B for A10(M/T/D)
+#define BOARD_GT2560_V4 1318 // Geeetech GT2560 Rev B for A10(M/T/D)
+#define BOARD_GT2560_V3_MC2 1319 // Geeetech GT2560 Rev B for Mecreator2
+#define BOARD_GT2560_V3_A20 1320 // Geeetech GT2560 Rev B for A20(M/T/D)
+#define BOARD_EINSTART_S 1321 // Einstart retrofit
+#define BOARD_WANHAO_ONEPLUS 1322 // Wanhao 0ne+ i3 Mini
+#define BOARD_LEAPFROG_XEED2015 1323 // Leapfrog Xeed 2015
+#define BOARD_PICA_REVB 1324 // PICA Shield (original version)
+#define BOARD_PICA 1325 // PICA Shield (rev C or later)
+#define BOARD_INTAMSYS40 1326 // Intamsys 4.0 (Funmat HT)
+#define BOARD_MALYAN_M180 1327 // Malyan M180 Mainboard Version 2 (no display function, direct G-code only)
+#define BOARD_GT2560_V4_A20 1328 // Geeetech GT2560 Rev B for A20(M/T/D)
+#define BOARD_PROTONEER_CNC_SHIELD_V3 1329 // Mega controller & Protoneer CNC Shield V3.00
+#define BOARD_WEEDO_62A 1330 // WEEDO 62A board (TINA2, Monoprice Cadet, etc.)
+
+//
+// ATmega1281, ATmega2561
+//
+
+#define BOARD_MINITRONICS 1400 // Minitronics v1.0/1.1
+#define BOARD_SILVER_GATE 1401 // Silvergate v1.0
+
+//
+// Sanguinololu and Derivatives - ATmega644P, ATmega1284P
+//
+
+#define BOARD_SANGUINOLOLU_11 1500 // Sanguinololu < 1.2
+#define BOARD_SANGUINOLOLU_12 1501 // Sanguinololu 1.2 and above
+#define BOARD_MELZI 1502 // Melzi
+#define BOARD_MELZI_V2 1503 // Melzi V2
+#define BOARD_MELZI_MAKR3D 1504 // Melzi with ATmega1284 (MaKr3d version)
+#define BOARD_MELZI_CREALITY 1505 // Melzi Creality3D (for CR-10 etc)
+#define BOARD_MELZI_MALYAN 1506 // Melzi Malyan M150
+#define BOARD_MELZI_TRONXY 1507 // Tronxy X5S
+#define BOARD_STB_11 1508 // STB V1.1
+#define BOARD_AZTEEG_X1 1509 // Azteeg X1
+#define BOARD_ANET_10 1510 // Anet 1.0 (Melzi clone)
+#define BOARD_ZMIB_V2 1511 // ZoneStar ZMIB V2
+
+//
+// Other ATmega644P, ATmega644, ATmega1284P
+//
+
+#define BOARD_GEN3_MONOLITHIC 1600 // Gen3 Monolithic Electronics
+#define BOARD_GEN3_PLUS 1601 // Gen3+
+#define BOARD_GEN6 1602 // Gen6
+#define BOARD_GEN6_DELUXE 1603 // Gen6 deluxe
+#define BOARD_GEN7_CUSTOM 1604 // Gen7 custom (Alfons3 Version) https://github.com/Alfons3/Generation_7_Electronics
+#define BOARD_GEN7_12 1605 // Gen7 v1.1, v1.2
+#define BOARD_GEN7_13 1606 // Gen7 v1.3
+#define BOARD_GEN7_14 1607 // Gen7 v1.4
+#define BOARD_OMCA_A 1608 // Alpha OMCA
+#define BOARD_OMCA 1609 // Final OMCA
+#define BOARD_SETHI 1610 // Sethi 3D_1
+
+//
+// Teensyduino - AT90USB1286, AT90USB1286P
+//
+
+#define BOARD_TEENSYLU 1700 // Teensylu
+#define BOARD_PRINTRBOARD 1701 // Printrboard (AT90USB1286)
+#define BOARD_PRINTRBOARD_REVF 1702 // Printrboard Revision F (AT90USB1286)
+#define BOARD_BRAINWAVE 1703 // Brainwave (AT90USB646)
+#define BOARD_BRAINWAVE_PRO 1704 // Brainwave Pro (AT90USB1286)
+#define BOARD_SAV_MKI 1705 // SAV Mk-I (AT90USB1286)
+#define BOARD_TEENSY2 1706 // Teensy++2.0 (AT90USB1286)
+#define BOARD_5DPRINT 1707 // 5DPrint D8 Driver Board
+
+//
+// LPC1768 ARM Cortex M3
+//
+
+#define BOARD_RAMPS_14_RE_ARM_EFB 2000 // Re-ARM with RAMPS 1.4 (Power outputs: Hotend, Fan, Bed)
+#define BOARD_RAMPS_14_RE_ARM_EEB 2001 // Re-ARM with RAMPS 1.4 (Power outputs: Hotend0, Hotend1, Bed)
+#define BOARD_RAMPS_14_RE_ARM_EFF 2002 // Re-ARM with RAMPS 1.4 (Power outputs: Hotend, Fan0, Fan1)
+#define BOARD_RAMPS_14_RE_ARM_EEF 2003 // Re-ARM with RAMPS 1.4 (Power outputs: Hotend0, Hotend1, Fan)
+#define BOARD_RAMPS_14_RE_ARM_SF 2004 // Re-ARM with RAMPS 1.4 (Power outputs: Spindle, Controller Fan)
+#define BOARD_MKS_SBASE 2005 // MKS-Sbase
+#define BOARD_AZSMZ_MINI 2006 // AZSMZ Mini
+#define BOARD_BIQU_BQ111_A4 2007 // BIQU BQ111-A4
+#define BOARD_SELENA_COMPACT 2008 // Selena Compact
+#define BOARD_BIQU_B300_V1_0 2009 // BIQU B300_V1.0
+#define BOARD_MKS_SGEN_L 2010 // MKS-SGen-L
+#define BOARD_GMARSH_X6_REV1 2011 // GMARSH X6, revision 1 prototype
+#define BOARD_BTT_SKR_V1_1 2012 // BigTreeTech SKR v1.1
+#define BOARD_BTT_SKR_V1_3 2013 // BigTreeTech SKR v1.3
+#define BOARD_BTT_SKR_V1_4 2014 // BigTreeTech SKR v1.4
+#define BOARD_EMOTRONIC 2015 // eMotion-Tech eMotronic
+
+//
+// LPC1769 ARM Cortex M3
+//
+
+#define BOARD_MKS_SGEN 2500 // MKS-SGen
+#define BOARD_AZTEEG_X5_GT 2501 // Azteeg X5 GT
+#define BOARD_AZTEEG_X5_MINI 2502 // Azteeg X5 Mini
+#define BOARD_AZTEEG_X5_MINI_WIFI 2503 // Azteeg X5 Mini Wifi
+#define BOARD_COHESION3D_REMIX 2504 // Cohesion3D ReMix
+#define BOARD_COHESION3D_MINI 2505 // Cohesion3D Mini
+#define BOARD_SMOOTHIEBOARD 2506 // Smoothieboard
+#define BOARD_TH3D_EZBOARD 2507 // TH3D EZBoard v1.0
+#define BOARD_BTT_SKR_V1_4_TURBO 2508 // BigTreeTech SKR v1.4 TURBO
+#define BOARD_MKS_SGEN_L_V2 2509 // MKS SGEN_L V2
+#define BOARD_BTT_SKR_E3_TURBO 2510 // BigTreeTech SKR E3 Turbo
+#define BOARD_FLY_CDY 2511 // FLYmaker FLY CDY
+
+//
+// SAM3X8E ARM Cortex M3
+//
+
+#define BOARD_DUE3DOM 3000 // DUE3DOM for Arduino DUE
+#define BOARD_DUE3DOM_MINI 3001 // DUE3DOM MINI for Arduino DUE
+#define BOARD_RADDS 3002 // RADDS
+#define BOARD_RAMPS_FD_V1 3003 // RAMPS-FD v1
+#define BOARD_RAMPS_FD_V2 3004 // RAMPS-FD v2
+#define BOARD_RAMPS_SMART_EFB 3005 // RAMPS-SMART (Power outputs: Hotend, Fan, Bed)
+#define BOARD_RAMPS_SMART_EEB 3006 // RAMPS-SMART (Power outputs: Hotend0, Hotend1, Bed)
+#define BOARD_RAMPS_SMART_EFF 3007 // RAMPS-SMART (Power outputs: Hotend, Fan0, Fan1)
+#define BOARD_RAMPS_SMART_EEF 3008 // RAMPS-SMART (Power outputs: Hotend0, Hotend1, Fan)
+#define BOARD_RAMPS_SMART_SF 3009 // RAMPS-SMART (Power outputs: Spindle, Controller Fan)
+#define BOARD_RAMPS_DUO_EFB 3010 // RAMPS Duo (Power outputs: Hotend, Fan, Bed)
+#define BOARD_RAMPS_DUO_EEB 3011 // RAMPS Duo (Power outputs: Hotend0, Hotend1, Bed)
+#define BOARD_RAMPS_DUO_EFF 3012 // RAMPS Duo (Power outputs: Hotend, Fan0, Fan1)
+#define BOARD_RAMPS_DUO_EEF 3013 // RAMPS Duo (Power outputs: Hotend0, Hotend1, Fan)
+#define BOARD_RAMPS_DUO_SF 3014 // RAMPS Duo (Power outputs: Spindle, Controller Fan)
+#define BOARD_RAMPS4DUE_EFB 3015 // RAMPS4DUE (Power outputs: Hotend, Fan, Bed)
+#define BOARD_RAMPS4DUE_EEB 3016 // RAMPS4DUE (Power outputs: Hotend0, Hotend1, Bed)
+#define BOARD_RAMPS4DUE_EFF 3017 // RAMPS4DUE (Power outputs: Hotend, Fan0, Fan1)
+#define BOARD_RAMPS4DUE_EEF 3018 // RAMPS4DUE (Power outputs: Hotend0, Hotend1, Fan)
+#define BOARD_RAMPS4DUE_SF 3019 // RAMPS4DUE (Power outputs: Spindle, Controller Fan)
+#define BOARD_RURAMPS4D_11 3020 // RuRAMPS4Duo v1.1
+#define BOARD_RURAMPS4D_13 3021 // RuRAMPS4Duo v1.3
+#define BOARD_ULTRATRONICS_PRO 3022 // ReprapWorld Ultratronics Pro V1.0
+#define BOARD_ARCHIM1 3023 // UltiMachine Archim1 (with DRV8825 drivers)
+#define BOARD_ARCHIM2 3024 // UltiMachine Archim2 (with TMC2130 drivers)
+#define BOARD_ALLIGATOR 3025 // Alligator Board R2
+#define BOARD_CNCONTROLS_15D 3026 // Cartesio CN Controls V15 on DUE
+#define BOARD_KRATOS32 3027 // K.3D Kratos32 (Arduino Due Shield)
+
+//
+// SAM3X8C ARM Cortex M3
+//
+
+#define BOARD_PRINTRBOARD_G2 3100 // Printrboard G2
+#define BOARD_ADSK 3101 // Arduino DUE Shield Kit (ADSK)
+
+//
+// STM32 ARM Cortex-M3
+//
+
+#define BOARD_MALYAN_M200_V2 4000 // STM32F070CB controller
+#define BOARD_MALYAN_M300 4001 // STM32F070-based delta
+#define BOARD_STM32F103RE 4002 // STM32F103RE Libmaple-based STM32F1 controller
+#define BOARD_MALYAN_M200 4003 // STM32C8 Libmaple-based STM32F1 controller
+#define BOARD_STM3R_MINI 4004 // STM32F103RE Libmaple-based STM32F1 controller
+#define BOARD_GTM32_PRO_VB 4005 // STM32F103VE controller
+#define BOARD_GTM32_MINI 4006 // STM32F103VE controller
+#define BOARD_GTM32_MINI_A30 4007 // STM32F103VE controller
+#define BOARD_GTM32_REV_B 4008 // STM32F103VE controller
+#define BOARD_MORPHEUS 4009 // STM32F103C8 / STM32F103CB Libmaple-based STM32F1 controller
+#define BOARD_CHITU3D 4010 // Chitu3D (STM32F103RE)
+#define BOARD_MKS_ROBIN 4011 // MKS Robin (STM32F103ZE)
+#define BOARD_MKS_ROBIN_MINI 4012 // MKS Robin Mini (STM32F103VE)
+#define BOARD_MKS_ROBIN_NANO 4013 // MKS Robin Nano (STM32F103VE)
+#define BOARD_MKS_ROBIN_NANO_V2 4014 // MKS Robin Nano V2 (STM32F103VE)
+#define BOARD_MKS_ROBIN_LITE 4015 // MKS Robin Lite/Lite2 (STM32F103RC)
+#define BOARD_MKS_ROBIN_LITE3 4016 // MKS Robin Lite3 (STM32F103RC)
+#define BOARD_MKS_ROBIN_PRO 4017 // MKS Robin Pro (STM32F103ZE)
+#define BOARD_MKS_ROBIN_E3 4018 // MKS Robin E3 (STM32F103RC)
+#define BOARD_MKS_ROBIN_E3_V1_1 4019 // MKS Robin E3 V1.1 (STM32F103RC)
+#define BOARD_MKS_ROBIN_E3D 4020 // MKS Robin E3D (STM32F103RC)
+#define BOARD_MKS_ROBIN_E3D_V1_1 4021 // MKS Robin E3D V1.1 (STM32F103RC)
+#define BOARD_MKS_ROBIN_E3P 4022 // MKS Robin E3p (STM32F103VE)
+#define BOARD_BTT_EBB42_V1_1 4023 // BigTreeTech EBB42 V1.1 (STM32G0B1CB)
+#define BOARD_BTT_SKR_MINI_V1_1 4024 // BigTreeTech SKR Mini v1.1 (STM32F103RC)
+#define BOARD_BTT_SKR_MINI_E3_V1_0 4025 // BigTreeTech SKR Mini E3 (STM32F103RC)
+#define BOARD_BTT_SKR_MINI_E3_V1_2 4026 // BigTreeTech SKR Mini E3 V1.2 (STM32F103RC)
+#define BOARD_BTT_SKR_MINI_E3_V2_0 4027 // BigTreeTech SKR Mini E3 V2.0 (STM32F103RC / STM32F103RE)
+#define BOARD_BTT_SKR_MINI_E3_V3_0 4028 // BigTreeTech SKR Mini E3 V3.0 (STM32G0B1RE)
+#define BOARD_BTT_SKR_MINI_E3_V3_0_1 4029 // BigTreeTech SKR Mini E3 V3.0.1 (STM32F401RC)
+#define BOARD_BTT_SKR_MINI_MZ_V1_0 4030 // BigTreeTech SKR Mini MZ V1.0 (STM32F103RC)
+#define BOARD_BTT_SKR_E3_DIP 4031 // BigTreeTech SKR E3 DIP V1.0 (STM32F103RC / STM32F103RE)
+#define BOARD_BTT_SKR_CR6 4032 // BigTreeTech SKR CR6 v1.0 (STM32F103RE)
+#define BOARD_JGAURORA_A5S_A1 4033 // JGAurora A5S A1 (STM32F103ZE)
+#define BOARD_FYSETC_AIO_II 4034 // FYSETC AIO_II (STM32F103RC)
+#define BOARD_FYSETC_CHEETAH 4035 // FYSETC Cheetah (STM32F103RC)
+#define BOARD_FYSETC_CHEETAH_V12 4036 // FYSETC Cheetah V1.2 (STM32F103RC)
+#define BOARD_LONGER3D_LK 4037 // Longer3D LK1/2 - Alfawise U20/U20+/U30 (STM32F103VE)
+#define BOARD_CCROBOT_MEEB_3DP 4038 // ccrobot-online.com MEEB_3DP (STM32F103RC)
+#define BOARD_CHITU3D_V5 4039 // Chitu3D TronXY X5SA V5 Board (STM32F103ZE)
+#define BOARD_CHITU3D_V6 4040 // Chitu3D TronXY X5SA V6 Board (STM32F103ZE)
+#define BOARD_CHITU3D_V9 4041 // Chitu3D TronXY X5SA V9 Board (STM32F103ZE)
+#define BOARD_CREALITY_V4 4042 // Creality v4.x (STM32F103RC / STM32F103RE)
+#define BOARD_CREALITY_V422 4043 // Creality v4.2.2 (STM32F103RC / STM32F103RE)
+#define BOARD_CREALITY_V423 4044 // Creality v4.2.3 (STM32F103RC / STM32F103RE)
+#define BOARD_CREALITY_V425 4045 // Creality v4.2.5 (STM32F103RC / STM32F103RE)
+#define BOARD_CREALITY_V427 4046 // Creality v4.2.7 (STM32F103RC / STM32F103RE)
+#define BOARD_CREALITY_V4210 4047 // Creality v4.2.10 (STM32F103RC / STM32F103RE) as found in the CR-30
+#define BOARD_CREALITY_V431 4048 // Creality v4.3.1 (STM32F103RC / STM32F103RE)
+#define BOARD_CREALITY_V431_A 4049 // Creality v4.3.1a (STM32F103RC / STM32F103RE)
+#define BOARD_CREALITY_V431_B 4050 // Creality v4.3.1b (STM32F103RC / STM32F103RE)
+#define BOARD_CREALITY_V431_C 4051 // Creality v4.3.1c (STM32F103RC / STM32F103RE)
+#define BOARD_CREALITY_V431_D 4052 // Creality v4.3.1d (STM32F103RC / STM32F103RE)
+#define BOARD_CREALITY_V452 4053 // Creality v4.5.2 (STM32F103RC / STM32F103RE)
+#define BOARD_CREALITY_V453 4054 // Creality v4.5.3 (STM32F103RC / STM32F103RE)
+#define BOARD_CREALITY_V521 4055 // Creality v5.2.1 (STM32F103VE) as found in the SV04
+#define BOARD_CREALITY_V24S1 4056 // Creality v2.4.S1 (STM32F103RC / STM32F103RE) v101 as found in the Ender-7
+#define BOARD_CREALITY_V24S1_301 4057 // Creality v2.4.S1_301 (STM32F103RC / STM32F103RE) v301 as found in the Ender-3 S1
+#define BOARD_CREALITY_V25S1 4058 // Creality v2.5.S1 (STM32F103RE) as found in the CR-10 Smart Pro
+#define BOARD_TRIGORILLA_PRO 4059 // Trigorilla Pro (STM32F103ZE)
+#define BOARD_FLY_MINI 4060 // FLYmaker FLY MINI (STM32F103RC)
+#define BOARD_FLSUN_HISPEED 4061 // FLSUN HiSpeedV1 (STM32F103VE)
+#define BOARD_BEAST 4062 // STM32F103RE Libmaple-based controller
+#define BOARD_MINGDA_MPX_ARM_MINI 4063 // STM32F103ZE Mingda MD-16
+#define BOARD_GTM32_PRO_VD 4064 // STM32F103VE controller
+#define BOARD_ZONESTAR_ZM3E2 4065 // Zonestar ZM3E2 (STM32F103RC)
+#define BOARD_ZONESTAR_ZM3E4 4066 // Zonestar ZM3E4 V1 (STM32F103VC)
+#define BOARD_ZONESTAR_ZM3E4V2 4067 // Zonestar ZM3E4 V2 (STM32F103VC)
+#define BOARD_ERYONE_ERY32_MINI 4068 // Eryone Ery32 mini (STM32F103VE)
+#define BOARD_PANDA_PI_V29 4069 // Panda Pi V2.9 - Standalone (STM32F103RC)
+#define BOARD_VOXELAB_AQUILA 4070 // Voxelab Aquila (GD32F103Rx)
+
+//
+// ARM Cortex-M4F
+//
+
+#define BOARD_TEENSY31_32 4100 // Teensy3.1 and Teensy3.2
+#define BOARD_TEENSY35_36 4101 // Teensy3.5 and Teensy3.6
+
+//
+// STM32 ARM Cortex-M4F
+//
+
+#define BOARD_ARMED 4200 // Arm'ed STM32F4-based controller
+#define BOARD_RUMBA32_V1_0 4201 // RUMBA32 STM32F446VE based controller from Aus3D
+#define BOARD_RUMBA32_V1_1 4202 // RUMBA32 STM32F446VE based controller from Aus3D
+#define BOARD_RUMBA32_MKS 4203 // RUMBA32 STM32F446VE based controller from Makerbase
+#define BOARD_RUMBA32_BTT 4204 // RUMBA32 STM32F446VE based controller from BIGTREETECH
+#define BOARD_BLACK_STM32F407VE 4205 // BLACK_STM32F407VE
+#define BOARD_BLACK_STM32F407ZE 4206 // BLACK_STM32F407ZE
+#define BOARD_BTT_SKR_PRO_V1_1 4207 // BigTreeTech SKR Pro v1.1 (STM32F407ZG)
+#define BOARD_BTT_SKR_PRO_V1_2 4208 // BigTreeTech SKR Pro v1.2 (STM32F407ZG)
+#define BOARD_BTT_BTT002_V1_0 4209 // BigTreeTech BTT002 v1.0 (STM32F407VG)
+#define BOARD_BTT_E3_RRF 4210 // BigTreeTech E3 RRF (STM32F407VG)
+#define BOARD_BTT_SKR_V2_0_REV_A 4211 // BigTreeTech SKR v2.0 Rev A (STM32F407VG)
+#define BOARD_BTT_SKR_V2_0_REV_B 4212 // BigTreeTech SKR v2.0 Rev B (STM32F407VG/STM32F429VG)
+#define BOARD_BTT_GTR_V1_0 4213 // BigTreeTech GTR v1.0 (STM32F407IGT)
+#define BOARD_BTT_OCTOPUS_V1_0 4214 // BigTreeTech Octopus v1.0 (STM32F446ZE)
+#define BOARD_BTT_OCTOPUS_V1_1 4215 // BigTreeTech Octopus v1.1 (STM32F446ZE)
+#define BOARD_BTT_OCTOPUS_PRO_V1_0 4216 // BigTreeTech Octopus Pro v1.0 (STM32F446ZE / STM32F429ZG)
+#define BOARD_LERDGE_K 4217 // Lerdge K (STM32F407ZG)
+#define BOARD_LERDGE_S 4218 // Lerdge S (STM32F407VE)
+#define BOARD_LERDGE_X 4219 // Lerdge X (STM32F407VE)
+#define BOARD_VAKE403D 4220 // VAkE 403D (STM32F446VE)
+#define BOARD_FYSETC_S6 4221 // FYSETC S6 (STM32F446VE)
+#define BOARD_FYSETC_S6_V2_0 4222 // FYSETC S6 v2.0 (STM32F446VE)
+#define BOARD_FYSETC_SPIDER 4223 // FYSETC Spider (STM32F446VE)
+#define BOARD_FLYF407ZG 4224 // FLYmaker FLYF407ZG (STM32F407ZG)
+#define BOARD_MKS_ROBIN2 4225 // MKS_ROBIN2 (STM32F407ZE)
+#define BOARD_MKS_ROBIN_PRO_V2 4226 // MKS Robin Pro V2 (STM32F407VE)
+#define BOARD_MKS_ROBIN_NANO_V3 4227 // MKS Robin Nano V3 (STM32F407VG)
+#define BOARD_MKS_ROBIN_NANO_V3_1 4228 // MKS Robin Nano V3.1 (STM32F407VE)
+#define BOARD_MKS_MONSTER8_V1 4229 // MKS Monster8 V1 (STM32F407VE)
+#define BOARD_MKS_MONSTER8_V2 4230 // MKS Monster8 V2 (STM32F407VE)
+#define BOARD_ANET_ET4 4231 // ANET ET4 V1.x (STM32F407VG)
+#define BOARD_ANET_ET4P 4232 // ANET ET4P V1.x (STM32F407VG)
+#define BOARD_FYSETC_CHEETAH_V20 4233 // FYSETC Cheetah V2.0 (STM32F401RC)
+#define BOARD_TH3D_EZBOARD_V2 4234 // TH3D EZBoard v2.0 (STM32F405RG)
+#define BOARD_OPULO_LUMEN_REV3 4235 // Opulo Lumen PnP Controller REV3 (STM32F407VE / STM32F407VG)
+#define BOARD_MKS_ROBIN_NANO_V1_3_F4 4236 // MKS Robin Nano V1.3 and MKS Robin Nano-S V1.3 (STM32F407VE)
+#define BOARD_MKS_EAGLE 4237 // MKS Eagle (STM32F407VE)
+#define BOARD_ARTILLERY_RUBY 4238 // Artillery Ruby (STM32F401RC)
+#define BOARD_FYSETC_SPIDER_V2_2 4239 // FYSETC Spider V2.2 (STM32F446VE)
+#define BOARD_CREALITY_V24S1_301F4 4240 // Creality v2.4.S1_301F4 (STM32F401RC) as found in the Ender-3 S1 F4
+#define BOARD_OPULO_LUMEN_REV4 4241 // Opulo Lumen PnP Controller REV4 (STM32F407VE / STM32F407VG)
+#define BOARD_FYSETC_SPIDER_KING407 4242 // FYSETC Spider King407 (STM32F407ZG)
+#define BOARD_MKS_SKIPR_V1 4243 // MKS SKIPR v1.0 all-in-one board (STM32F407VE)
+#define BOARD_TRONXY_V10 4244 // TRONXY V10 (STM32F446ZE)
+
+//
+// ARM Cortex M7
+//
+
+#define BOARD_REMRAM_V1 5000 // RemRam v1
+#define BOARD_TEENSY41 5001 // Teensy 4.1
+#define BOARD_T41U5XBB 5002 // T41U5XBB Teensy 4.1 breakout board
+#define BOARD_NUCLEO_F767ZI 5003 // ST NUCLEO-F767ZI Dev Board
+#define BOARD_BTT_SKR_SE_BX_V2 5004 // BigTreeTech SKR SE BX V2.0 (STM32H743II)
+#define BOARD_BTT_SKR_SE_BX_V3 5005 // BigTreeTech SKR SE BX V3.0 (STM32H743II)
+#define BOARD_BTT_SKR_V3_0 5006 // BigTreeTech SKR V3.0 (STM32H743VG)
+#define BOARD_BTT_SKR_V3_0_EZ 5007 // BigTreeTech SKR V3.0 EZ (STM32H743VG)
+
+//
+// Espressif ESP32 WiFi
+//
+
+#define BOARD_ESPRESSIF_ESP32 6000 // Generic ESP32
+#define BOARD_MRR_ESPA 6001 // MRR ESPA based on ESP32 (native pins only)
+#define BOARD_MRR_ESPE 6002 // MRR ESPE based on ESP32 (with I2S stepper stream)
+#define BOARD_E4D_BOX 6003 // E4d@BOX
+#define BOARD_RESP32_CUSTOM 6004 // Rutilea ESP32 custom board
+#define BOARD_FYSETC_E4 6005 // FYSETC E4
+#define BOARD_PANDA_ZHU 6006 // Panda_ZHU
+#define BOARD_PANDA_M4 6007 // Panda_M4
+#define BOARD_MKS_TINYBEE 6008 // MKS TinyBee based on ESP32 (with I2S stepper stream)
+#define BOARD_ENWI_ESPNP 6009 // enwi ESPNP based on ESP32 (with I2S stepper stream)
+
+//
+// SAMD51 ARM Cortex M4
+//
+
+#define BOARD_AGCM4_RAMPS_144 6100 // RAMPS 1.4.4
+#define BOARD_BRICOLEMON_V1_0 6101 // Bricolemon
+#define BOARD_BRICOLEMON_LITE_V1_0 6102 // Bricolemon Lite
+
+//
+// SAMD21 ARM Cortex M4
+//
+
+#define BOARD_MINITRONICS20 6103 // Minitronics v2.0
+
+//
+// Custom board
+//
+
+#define BOARD_CUSTOM 9998 // Custom pins definition for development and/or rare boards
+
+//
+// Simulations
+//
+
+#define BOARD_LINUX_RAMPS 9999
+
+#define _MB_1(B) (defined(BOARD_##B) && MOTHERBOARD==BOARD_##B)
+#define MB(V...) DO(MB,||,V)
diff --git a/Marlin/src/core/bug_on.h b/Marlin/src/core/bug_on.h
new file mode 100644
index 0000000000..7f1243ed40
--- /dev/null
+++ b/Marlin/src/core/bug_on.h
@@ -0,0 +1,39 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Copyright (c) 2021 X-Ryl669 [https://blog.cyril.by]
+ *
+ * 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
+
+// We need SERIAL_ECHOPGM and macros.h
+#include "serial.h"
+
+#if ENABLED(POSTMORTEM_DEBUGGING)
+ // Useful macro for stopping the CPU on an unexpected condition
+ // This is used like SERIAL_ECHOPGM, that is: a key-value call of the local variables you want
+ // to dump to the serial port before stopping the CPU.
+ // \/ Don't replace by SERIAL_ECHOPGM since ONLY_FILENAME cannot be transformed to a PGM string on Arduino and it breaks building
+ #define BUG_ON(V...) do { SERIAL_ECHO(ONLY_FILENAME); SERIAL_ECHO(__LINE__); SERIAL_ECHOLNPGM(": "); SERIAL_ECHOLNPGM(V); SERIAL_FLUSHTX(); *(char*)0 = 42; } while(0)
+#elif ENABLED(MARLIN_DEV_MODE)
+ // Don't stop the CPU here, but at least dump the bug on the serial port
+ // \/ Don't replace by SERIAL_ECHOPGM since ONLY_FILENAME cannot be transformed to a PGM string on Arduino and it breaks building
+ #define BUG_ON(V...) do { SERIAL_ECHO(ONLY_FILENAME); SERIAL_ECHO(__LINE__); SERIAL_ECHOLNPGM(": BUG!"); SERIAL_ECHOLNPGM(V); SERIAL_FLUSHTX(); } while(0)
+#else
+ // Release mode, let's ignore the bug
+ #define BUG_ON(V...) NOOP
+#endif
diff --git a/Marlin/src/core/debug_out.h b/Marlin/src/core/debug_out.h
new file mode 100644
index 0000000000..eb1c91e507
--- /dev/null
+++ b/Marlin/src/core/debug_out.h
@@ -0,0 +1,120 @@
+/**
+ * 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 .
+ *
+ */
+
+//
+// Serial aliases for debugging.
+// Include this header after defining DEBUG_OUT
+// (or not) in a given .cpp file
+//
+
+#undef DEBUG_SECTION
+#undef DEBUG_ECHO_START
+#undef DEBUG_ERROR_START
+#undef DEBUG_CHAR
+#undef DEBUG_ECHO
+#undef DEBUG_DECIMAL
+#undef DEBUG_ECHO_F
+#undef DEBUG_ECHOLN
+#undef DEBUG_ECHOPGM
+#undef DEBUG_ECHOLNPGM
+#undef DEBUG_ECHOF
+#undef DEBUG_ECHOLNF
+#undef DEBUG_ECHOPGM_P
+#undef DEBUG_ECHOLNPGM_P
+#undef DEBUG_ECHOPAIR_F
+#undef DEBUG_ECHOPAIR_F_P
+#undef DEBUG_ECHOLNPAIR_F
+#undef DEBUG_ECHOLNPAIR_F_P
+#undef DEBUG_ECHO_MSG
+#undef DEBUG_ERROR_MSG
+#undef DEBUG_EOL
+#undef DEBUG_FLUSH
+#undef DEBUG_POS
+#undef DEBUG_XYZ
+#undef DEBUG_DELAY
+#undef DEBUG_SYNCHRONIZE
+
+#if DEBUG_OUT
+
+ #include "debug_section.h"
+ #define DEBUG_SECTION(N,S,D) SectionLog N(F(S),D)
+
+ #define DEBUG_ECHO_START SERIAL_ECHO_START
+ #define DEBUG_ERROR_START SERIAL_ERROR_START
+ #define DEBUG_CHAR SERIAL_CHAR
+ #define DEBUG_ECHO SERIAL_ECHO
+ #define DEBUG_DECIMAL SERIAL_DECIMAL
+ #define DEBUG_ECHO_F SERIAL_ECHO_F
+ #define DEBUG_ECHOLN SERIAL_ECHOLN
+ #define DEBUG_ECHOPGM SERIAL_ECHOPGM
+ #define DEBUG_ECHOLNPGM SERIAL_ECHOLNPGM
+ #define DEBUG_ECHOF SERIAL_ECHOF
+ #define DEBUG_ECHOLNF SERIAL_ECHOLNF
+ #define DEBUG_ECHOPGM SERIAL_ECHOPGM
+ #define DEBUG_ECHOPGM_P SERIAL_ECHOPGM_P
+ #define DEBUG_ECHOPAIR_F SERIAL_ECHOPAIR_F
+ #define DEBUG_ECHOPAIR_F_P SERIAL_ECHOPAIR_F_P
+ #define DEBUG_ECHOLNPGM SERIAL_ECHOLNPGM
+ #define DEBUG_ECHOLNPGM_P SERIAL_ECHOLNPGM_P
+ #define DEBUG_ECHOLNPAIR_F SERIAL_ECHOLNPAIR_F
+ #define DEBUG_ECHOLNPAIR_F_P SERIAL_ECHOLNPAIR_F_P
+ #define DEBUG_ECHO_MSG SERIAL_ECHO_MSG
+ #define DEBUG_ERROR_MSG SERIAL_ERROR_MSG
+ #define DEBUG_EOL SERIAL_EOL
+ #define DEBUG_FLUSH SERIAL_FLUSH
+ #define DEBUG_POS SERIAL_POS
+ #define DEBUG_XYZ SERIAL_XYZ
+ #define DEBUG_DELAY(ms) serial_delay(ms)
+ #define DEBUG_SYNCHRONIZE() planner.synchronize()
+
+#else
+
+ #define DEBUG_SECTION(...) NOOP
+ #define DEBUG_ECHO_START() NOOP
+ #define DEBUG_ERROR_START() NOOP
+ #define DEBUG_CHAR(...) NOOP
+ #define DEBUG_ECHO(...) NOOP
+ #define DEBUG_DECIMAL(...) NOOP
+ #define DEBUG_ECHO_F(...) NOOP
+ #define DEBUG_ECHOLN(...) NOOP
+ #define DEBUG_ECHOPGM(...) NOOP
+ #define DEBUG_ECHOLNPGM(...) NOOP
+ #define DEBUG_ECHOF(...) NOOP
+ #define DEBUG_ECHOLNF(...) NOOP
+ #define DEBUG_ECHOPGM_P(...) NOOP
+ #define DEBUG_ECHOLNPGM_P(...) NOOP
+ #define DEBUG_ECHOPAIR_F(...) NOOP
+ #define DEBUG_ECHOPAIR_F_P(...) NOOP
+ #define DEBUG_ECHOLNPAIR_F(...) NOOP
+ #define DEBUG_ECHOLNPAIR_F_P(...) NOOP
+ #define DEBUG_ECHO_MSG(...) NOOP
+ #define DEBUG_ERROR_MSG(...) NOOP
+ #define DEBUG_EOL() NOOP
+ #define DEBUG_FLUSH() NOOP
+ #define DEBUG_POS(...) NOOP
+ #define DEBUG_XYZ(...) NOOP
+ #define DEBUG_DELAY(...) NOOP
+ #define DEBUG_SYNCHRONIZE() NOOP
+
+#endif
+
+#undef DEBUG_OUT
diff --git a/Marlin/src/core/debug_section.h b/Marlin/src/core/debug_section.h
new file mode 100644
index 0000000000..6e23d9e4ed
--- /dev/null
+++ b/Marlin/src/core/debug_section.h
@@ -0,0 +1,49 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include "serial.h"
+#include "../module/motion.h"
+
+class SectionLog {
+public:
+ SectionLog(FSTR_P const fmsg=nullptr, bool inbug=true) {
+ the_msg = fmsg;
+ if ((debug = inbug)) echo_msg(F(">>>"));
+ }
+
+ ~SectionLog() { if (debug) echo_msg(F("<<<")); }
+
+private:
+ FSTR_P the_msg;
+ bool debug;
+
+ void echo_msg(FSTR_P const fpre) {
+ SERIAL_ECHOF(fpre);
+ if (the_msg) {
+ SERIAL_CHAR(' ');
+ SERIAL_ECHOF(the_msg);
+ }
+ SERIAL_CHAR(' ');
+ print_pos(current_position);
+ }
+};
diff --git a/Marlin/src/core/drivers.h b/Marlin/src/core/drivers.h
new file mode 100644
index 0000000000..72a7d1f4b7
--- /dev/null
+++ b/Marlin/src/core/drivers.h
@@ -0,0 +1,193 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+//
+// Included by MarlinConfigPre.h ahead of Configuration_adv.h.
+// Don't use #if in this file for anything not defined early!
+//
+
+#define _A4988 0x4988
+#define _A5984 0x5984
+#define _DRV8825 0x8825
+#define _LV8729 0x8729
+#define _TB6560 0x6560
+#define _TB6600 0x6600
+#define _TMC2100 0x2100
+#define _TMC2130 0x2130A
+#define _TMC2130_STANDALONE 0x2130B
+#define _TMC2160 0x2160A
+#define _TMC2160_STANDALONE 0x2160B
+#define _TMC2208 0x2208A
+#define _TMC2208_STANDALONE 0x2208B
+#define _TMC2209 0x2209A
+#define _TMC2209_STANDALONE 0x2209B
+#define _TMC26X 0x2600A
+#define _TMC26X_STANDALONE 0x2600B
+#define _TMC2660 0x2660A
+#define _TMC2660_STANDALONE 0x2660B
+#define _TMC5130 0x5130A
+#define _TMC5130_STANDALONE 0x5130B
+#define _TMC5160 0x5160A
+#define _TMC5160_STANDALONE 0x5160B
+
+#define _DRIVER_ID(V) _CAT(_, V)
+#define _AXIS_DRIVER_TYPE(A,T) (_DRIVER_ID(A##_DRIVER_TYPE) == _DRIVER_ID(T))
+
+#define AXIS_DRIVER_TYPE_X(T) _AXIS_DRIVER_TYPE(X,T)
+#define AXIS_DRIVER_TYPE_Y(T) _AXIS_DRIVER_TYPE(Y,T)
+#define AXIS_DRIVER_TYPE_Z(T) _AXIS_DRIVER_TYPE(Z,T)
+#define AXIS_DRIVER_TYPE_I(T) _AXIS_DRIVER_TYPE(I,T)
+#define AXIS_DRIVER_TYPE_J(T) _AXIS_DRIVER_TYPE(J,T)
+#define AXIS_DRIVER_TYPE_K(T) _AXIS_DRIVER_TYPE(K,T)
+#define AXIS_DRIVER_TYPE_U(T) _AXIS_DRIVER_TYPE(U,T)
+#define AXIS_DRIVER_TYPE_V(T) _AXIS_DRIVER_TYPE(V,T)
+#define AXIS_DRIVER_TYPE_W(T) _AXIS_DRIVER_TYPE(W,T)
+
+#define AXIS_DRIVER_TYPE_X2(T) (HAS_X2_STEPPER && _AXIS_DRIVER_TYPE(X2,T))
+#define AXIS_DRIVER_TYPE_Y2(T) (HAS_DUAL_Y_STEPPERS && _AXIS_DRIVER_TYPE(Y2,T))
+#define AXIS_DRIVER_TYPE_Z2(T) (NUM_Z_STEPPERS >= 2 && _AXIS_DRIVER_TYPE(Z2,T))
+#define AXIS_DRIVER_TYPE_Z3(T) (NUM_Z_STEPPERS >= 3 && _AXIS_DRIVER_TYPE(Z3,T))
+#define AXIS_DRIVER_TYPE_Z4(T) (NUM_Z_STEPPERS >= 4 && _AXIS_DRIVER_TYPE(Z4,T))
+
+#define AXIS_DRIVER_TYPE_E(N,T) (E_STEPPERS > N && _AXIS_DRIVER_TYPE(E##N,T))
+#define AXIS_DRIVER_TYPE_E0(T) AXIS_DRIVER_TYPE_E(0,T)
+#define AXIS_DRIVER_TYPE_E1(T) AXIS_DRIVER_TYPE_E(1,T)
+#define AXIS_DRIVER_TYPE_E2(T) AXIS_DRIVER_TYPE_E(2,T)
+#define AXIS_DRIVER_TYPE_E3(T) AXIS_DRIVER_TYPE_E(3,T)
+#define AXIS_DRIVER_TYPE_E4(T) AXIS_DRIVER_TYPE_E(4,T)
+#define AXIS_DRIVER_TYPE_E5(T) AXIS_DRIVER_TYPE_E(5,T)
+#define AXIS_DRIVER_TYPE_E6(T) AXIS_DRIVER_TYPE_E(6,T)
+#define AXIS_DRIVER_TYPE_E7(T) AXIS_DRIVER_TYPE_E(7,T)
+
+#define AXIS_DRIVER_TYPE(A,T) AXIS_DRIVER_TYPE_##A(T)
+
+#define _OR_ADTE(N,T) || AXIS_DRIVER_TYPE_E(N,T)
+#define HAS_E_DRIVER(T) (0 RREPEAT2(E_STEPPERS, _OR_ADTE, T))
+
+#define HAS_DRIVER(T) ( AXIS_DRIVER_TYPE_X(T) || AXIS_DRIVER_TYPE_Y(T) || AXIS_DRIVER_TYPE_Z(T) \
+ || AXIS_DRIVER_TYPE_I(T) || AXIS_DRIVER_TYPE_J(T) || AXIS_DRIVER_TYPE_K(T) \
+ || AXIS_DRIVER_TYPE_U(T) || AXIS_DRIVER_TYPE_V(T) || AXIS_DRIVER_TYPE_W(T) \
+ || AXIS_DRIVER_TYPE_X2(T) || AXIS_DRIVER_TYPE_Y2(T) || AXIS_DRIVER_TYPE_Z2(T) \
+ || AXIS_DRIVER_TYPE_Z3(T) || AXIS_DRIVER_TYPE_Z4(T) || HAS_E_DRIVER(T) )
+
+//
+// Trinamic Stepper Drivers
+//
+
+// Test for supported TMC drivers that require advanced configuration
+// Does not match standalone configurations
+#if ( HAS_DRIVER(TMC2130) || HAS_DRIVER(TMC2160) \
+ || HAS_DRIVER(TMC2208) || HAS_DRIVER(TMC2209) \
+ || HAS_DRIVER(TMC2660) \
+ || HAS_DRIVER(TMC5130) || HAS_DRIVER(TMC5160) )
+ #define HAS_TRINAMIC_CONFIG 1
+#endif
+
+#define HAS_TRINAMIC HAS_TRINAMIC_CONFIG
+
+#if ( HAS_DRIVER(TMC2130_STANDALONE) || HAS_DRIVER(TMC2160_STANDALONE) \
+ || HAS_DRIVER(TMC2208_STANDALONE) || HAS_DRIVER(TMC2209_STANDALONE) \
+ || HAS_DRIVER(TMC26X_STANDALONE) || HAS_DRIVER(TMC2660_STANDALONE) \
+ || HAS_DRIVER(TMC5130_STANDALONE) || HAS_DRIVER(TMC5160_STANDALONE) )
+ #define HAS_TRINAMIC_STANDALONE 1
+#endif
+
+#if HAS_DRIVER(TMC2130) || HAS_DRIVER(TMC2160) || HAS_DRIVER(TMC5130) || HAS_DRIVER(TMC5160)
+ #define HAS_TMCX1X0 1
+#endif
+
+#if HAS_DRIVER(TMC2208) || HAS_DRIVER(TMC2209)
+ #define HAS_TMC220x 1
+#endif
+
+#define AXIS_IS_TMC(A) ( AXIS_DRIVER_TYPE(A,TMC2130) || AXIS_DRIVER_TYPE(A,TMC2160) \
+ || AXIS_DRIVER_TYPE(A,TMC2208) || AXIS_DRIVER_TYPE(A,TMC2209) \
+ || AXIS_DRIVER_TYPE(A,TMC2660) \
+ || AXIS_DRIVER_TYPE(A,TMC5130) || AXIS_DRIVER_TYPE(A,TMC5160) )
+
+#define AXIS_IS_TMC_CONFIG(A) ( AXIS_IS_TMC(A) || AXIS_DRIVER_TYPE(A,TMC26X) )
+
+// Test for a driver that uses SPI - this allows checking whether a _CS_ pin
+// is considered sensitive
+#define AXIS_HAS_SPI(A) ( AXIS_DRIVER_TYPE(A,TMC2130) || AXIS_DRIVER_TYPE(A,TMC2160) \
+ || AXIS_DRIVER_TYPE(A,TMC26X) || AXIS_DRIVER_TYPE(A,TMC2660) \
+ || AXIS_DRIVER_TYPE(A,TMC5130) || AXIS_DRIVER_TYPE(A,TMC5160) )
+
+#define AXIS_HAS_UART(A) ( AXIS_DRIVER_TYPE(A,TMC2208) || AXIS_DRIVER_TYPE(A,TMC2209) )
+
+#define AXIS_HAS_RXTX AXIS_HAS_UART
+
+#define AXIS_HAS_HW_SERIAL(A) ( AXIS_HAS_UART(A) && defined(A##_HARDWARE_SERIAL) )
+#define AXIS_HAS_SW_SERIAL(A) ( AXIS_HAS_UART(A) && !defined(A##_HARDWARE_SERIAL) )
+
+#define AXIS_HAS_STALLGUARD(A) ( AXIS_DRIVER_TYPE(A,TMC2130) || AXIS_DRIVER_TYPE(A,TMC2160) \
+ || AXIS_DRIVER_TYPE(A,TMC2209) \
+ || AXIS_DRIVER_TYPE(A,TMC2660) \
+ || AXIS_DRIVER_TYPE(A,TMC5130) || AXIS_DRIVER_TYPE(A,TMC5160) )
+
+#define AXIS_HAS_STEALTHCHOP(A) ( AXIS_DRIVER_TYPE(A,TMC2130) || AXIS_DRIVER_TYPE(A,TMC2160) \
+ || AXIS_DRIVER_TYPE(A,TMC2208) || AXIS_DRIVER_TYPE(A,TMC2209) \
+ || AXIS_DRIVER_TYPE(A,TMC5130) || AXIS_DRIVER_TYPE(A,TMC5160) )
+
+#define AXIS_HAS_SG_RESULT(A) ( AXIS_DRIVER_TYPE(A,TMC2130) || AXIS_DRIVER_TYPE(A,TMC2160) \
+ || AXIS_DRIVER_TYPE(A,TMC2208) || AXIS_DRIVER_TYPE(A,TMC2209) )
+
+#define AXIS_HAS_COOLSTEP(A) ( AXIS_DRIVER_TYPE(A,TMC2130) \
+ || AXIS_DRIVER_TYPE(A,TMC2209) \
+ || AXIS_DRIVER_TYPE(A,TMC5130) || AXIS_DRIVER_TYPE(A,TMC5160) )
+
+#define _OR_EAH(N,T) || AXIS_HAS_##T(E##N)
+#define E_AXIS_HAS(T) (0 _OR_EAH(0,T) _OR_EAH(1,T) _OR_EAH(2,T) _OR_EAH(3,T) _OR_EAH(4,T) _OR_EAH(5,T) _OR_EAH(6,T) _OR_EAH(7,T))
+
+#define ANY_AXIS_HAS(T) ( AXIS_HAS_##T(X) || AXIS_HAS_##T(X2) \
+ || AXIS_HAS_##T(Y) || AXIS_HAS_##T(Y2) \
+ || AXIS_HAS_##T(Z) || AXIS_HAS_##T(Z2) || AXIS_HAS_##T(Z3) || AXIS_HAS_##T(Z4) \
+ || AXIS_HAS_##T(I) || AXIS_HAS_##T(J) || AXIS_HAS_##T(K) \
+ || AXIS_HAS_##T(U) || AXIS_HAS_##T(V) || AXIS_HAS_##T(W) \
+ || E_AXIS_HAS(T) )
+
+#if ANY_AXIS_HAS(STEALTHCHOP)
+ #define HAS_STEALTHCHOP 1
+#endif
+#if ANY_AXIS_HAS(STALLGUARD)
+ #define HAS_STALLGUARD 1
+#endif
+#if ANY_AXIS_HAS(SG_RESULT)
+ #define HAS_SG_RESULT 1
+#endif
+#if ANY_AXIS_HAS(COOLSTEP)
+ #define HAS_COOLSTEP 1
+#endif
+#if ANY_AXIS_HAS(RXTX)
+ #define HAS_TMC_UART 1
+#endif
+#if ANY_AXIS_HAS(SPI)
+ #define HAS_TMC_SPI 1
+#endif
+
+//
+// TMC26XX Stepper Drivers
+//
+#if HAS_DRIVER(TMC26X)
+ #define HAS_TMC26X 1
+#endif
diff --git a/Marlin/src/core/language.h b/Marlin/src/core/language.h
new file mode 100644
index 0000000000..545f9df641
--- /dev/null
+++ b/Marlin/src/core/language.h
@@ -0,0 +1,613 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#include "../inc/MarlinConfig.h"
+
+#define _UxGT(a) a
+
+// Fallback if no language is set. DON'T CHANGE
+#ifndef LCD_LANGUAGE
+ #define LCD_LANGUAGE en
+#endif
+
+// For character-based LCD controllers (DISPLAY_CHARSET_HD44780)
+#define JAPANESE 1
+#define WESTERN 2
+#define CYRILLIC 3
+
+// NOTE: IF YOU CHANGE LANGUAGE FILES OR MERGE A FILE WITH CHANGES
+//
+// ==> ALWAYS TRY TO COMPILE MARLIN WITH/WITHOUT "ULTIPANEL" / "ULTRA_LCD" / "SDSUPPORT" #define IN "Configuration.h"
+// ==> ALSO TRY ALL AVAILABLE LANGUAGE OPTIONS
+// See also https://marlinfw.org/docs/development/lcd_language.html
+
+// Languages
+// an Aragonese
+// bg Bulgarian
+// ca Catalan
+// cz Czech
+// da Danish
+// de German
+// el Greek (Greece)
+// el_CY Greek (Cyprus)
+// en English
+// es Spanish
+// eu Basque-Euskera
+// fi Finnish
+// fr French
+// gl Galician
+// hr Croatian
+// hu Hungarian
+// it Italian
+// jp_kana Japanese
+// ko_KR Korean (South Korea)
+// nl Dutch
+// pl Polish
+// pt Portuguese
+// pt_br Portuguese (Brazilian)
+// ro Romanian
+// ru Russian
+// sk Slovak
+// sv Swedish
+// tr Turkish
+// uk Ukrainian
+// vi Vietnamese
+// zh_CN Chinese (Simplified)
+// zh_TW Chinese (Traditional)
+
+#ifdef DEFAULT_SOURCE_CODE_URL
+ #undef SOURCE_CODE_URL
+ #define SOURCE_CODE_URL DEFAULT_SOURCE_CODE_URL
+#endif
+
+#ifdef CUSTOM_MACHINE_NAME
+ #undef MACHINE_NAME
+ #define MACHINE_NAME CUSTOM_MACHINE_NAME
+#elif defined(DEFAULT_MACHINE_NAME)
+ #undef MACHINE_NAME
+ #define MACHINE_NAME DEFAULT_MACHINE_NAME
+#endif
+
+#ifndef MACHINE_UUID
+ #define MACHINE_UUID DEFAULT_MACHINE_UUID
+#endif
+
+#define MARLIN_WEBSITE_URL "marlinfw.org"
+
+//#if !defined(STRING_SPLASH_LINE3) && defined(WEBSITE_URL)
+// #define STRING_SPLASH_LINE3 WEBSITE_URL
+//#endif
+
+//
+// Common Serial Console Messages
+// Don't change these strings because serial hosts look for them.
+//
+
+#define STR_ENQUEUEING "enqueueing \""
+#define STR_POWERUP "PowerUp"
+#define STR_POWEROFF "PowerOff"
+#define STR_EXTERNAL_RESET " External Reset"
+#define STR_BROWNOUT_RESET " Brown out Reset"
+#define STR_WATCHDOG_RESET " Watchdog Reset"
+#define STR_SOFTWARE_RESET " Software Reset"
+#define STR_FREE_MEMORY " Free Memory: "
+#define STR_PLANNER_BUFFER_BYTES " PlannerBufferBytes: "
+#define STR_OK "ok"
+#define STR_WAIT "wait"
+#define STR_STATS "Stats: "
+#define STR_FILE_SAVED "Done saving file."
+#define STR_ERR_LINE_NO "Line Number is not Last Line Number+1, Last Line: "
+#define STR_ERR_CHECKSUM_MISMATCH "checksum mismatch, Last Line: "
+#define STR_ERR_NO_CHECKSUM "No Checksum with line number, Last Line: "
+#define STR_FILE_PRINTED "Done printing file"
+#define STR_NO_MEDIA "No media"
+#define STR_BEGIN_FILE_LIST "Begin file list"
+#define STR_END_FILE_LIST "End file list"
+#define STR_INVALID_EXTRUDER "Invalid extruder"
+#define STR_INVALID_E_STEPPER "Invalid E stepper"
+#define STR_E_STEPPER_NOT_SPECIFIED "E stepper not specified"
+#define STR_INVALID_SOLENOID "Invalid solenoid"
+#define STR_COUNT_X " Count X:"
+#define STR_COUNT_A " Count A:"
+#define STR_WATCHDOG_FIRED "Watchdog timeout. Reset required."
+#define STR_ERR_KILLED "Printer halted. kill() called!"
+#define STR_FLOWMETER_FAULT "Coolant flow fault. Flowmeter safety is active. Attention required."
+#define STR_ERR_STOPPED "Printer stopped due to errors. Fix the error and use M999 to restart. (Temperature is reset. Set it after restarting)"
+#define STR_ERR_SERIAL_MISMATCH "Serial status mismatch"
+#define STR_BUSY_PROCESSING "busy: processing"
+#define STR_BUSY_PAUSED_FOR_USER "busy: paused for user"
+#define STR_BUSY_PAUSED_FOR_INPUT "busy: paused for input"
+#define STR_Z_MOVE_COMP "Z_move_comp"
+#define STR_RESEND "Resend: "
+#define STR_UNKNOWN_COMMAND "Unknown command: \""
+#define STR_ACTIVE_EXTRUDER "Active Extruder: "
+#define STR_ERR_FANSPEED "Fan speed E"
+
+#define STR_PROBE_OFFSET "Probe Offset"
+#define STR_SKEW_MIN "min_skew_factor: "
+#define STR_SKEW_MAX "max_skew_factor: "
+#define STR_ERR_MATERIAL_INDEX "M145 S out of range (0-1)"
+#define STR_ERR_M421_PARAMETERS "M421 incorrect parameter usage"
+#define STR_ERR_BAD_PLANE_MODE "G5 requires XY plane mode"
+#define STR_ERR_MESH_XY "Mesh point out of range"
+#define STR_ERR_ARC_ARGS "G2/G3 bad parameters"
+#define STR_ERR_PROTECTED_PIN "Protected Pin"
+#define STR_ERR_M420_FAILED "Failed to enable Bed Leveling"
+#define STR_ERR_M428_TOO_FAR "Too far from reference point"
+#define STR_ERR_M303_DISABLED "PIDTEMP disabled"
+#define STR_M119_REPORT "Reporting endstop status"
+#define STR_ON "ON"
+#define STR_OFF "OFF"
+#define STR_ENDSTOP_HIT "TRIGGERED"
+#define STR_ENDSTOP_OPEN "open"
+#define STR_DUPLICATION_MODE "Duplication mode: "
+#define STR_SOFT_MIN " Min: "
+#define STR_SOFT_MAX " Max: "
+
+#define STR_SAVED_POS "Position saved"
+#define STR_RESTORING_POS "Restoring position"
+#define STR_INVALID_POS_SLOT "Invalid slot. Total: "
+#define STR_DONE "Done."
+
+#define STR_SD_CANT_OPEN_SUBDIR "Cannot open subdir "
+#define STR_SD_INIT_FAIL "No SD card"
+#define STR_SD_VOL_INIT_FAIL "volume.init failed"
+#define STR_SD_OPENROOT_FAIL "openRoot failed"
+#define STR_SD_CARD_OK "SD card ok"
+#define STR_SD_CARD_RELEASED "SD card released"
+#define STR_SD_WORKDIR_FAIL "workDir open failed"
+#define STR_SD_OPEN_FILE_FAIL "open failed, File: "
+#define STR_SD_FILE_OPENED "File opened: "
+#define STR_SD_SIZE " Size: "
+#define STR_SD_FILE_SELECTED "File selected"
+#define STR_SD_WRITE_TO_FILE "Writing to file: "
+#define STR_SD_PRINTING_BYTE "SD printing byte "
+#define STR_SD_NOT_PRINTING "Not SD printing"
+#define STR_SD_ERR_WRITE_TO_FILE "error writing to file"
+#define STR_SD_ERR_READ "SD read error"
+#define STR_SD_CANT_ENTER_SUBDIR "Cannot enter subdir: "
+
+#define STR_ENDSTOPS_HIT "endstops hit: "
+#define STR_ERR_COLD_EXTRUDE_STOP " cold extrusion prevented"
+#define STR_ERR_LONG_EXTRUDE_STOP " too long extrusion prevented"
+#define STR_ERR_HOTEND_TOO_COLD "Hotend too cold"
+#define STR_ERR_EEPROM_WRITE "Error writing to EEPROM!"
+
+#define STR_FILAMENT_CHANGE_HEAT_LCD "Press button to heat nozzle"
+#define STR_FILAMENT_CHANGE_INSERT_LCD "Insert filament and press button"
+#define STR_FILAMENT_CHANGE_WAIT_LCD "Press button to resume"
+#define STR_FILAMENT_CHANGE_HEAT_M108 "Send M108 to heat nozzle"
+#define STR_FILAMENT_CHANGE_INSERT_M108 "Insert filament and send M108"
+#define STR_FILAMENT_CHANGE_WAIT_M108 "Send M108 to resume"
+
+#define STR_STOP_PRE "!! STOP called because of "
+#define STR_STOP_POST " error - restart with M999"
+#define STR_STOP_BLTOUCH "BLTouch"
+#define STR_STOP_UNHOMED "unhomed"
+#define STR_KILL_PRE "!! KILL caused by "
+#define STR_KILL_INACTIVE_TIME "too much inactive time - current command: "
+#define STR_KILL_BUTTON "KILL button/pin"
+
+// temperature.cpp strings
+#define STR_PID_AUTOTUNE "PID Autotune"
+#define STR_PID_AUTOTUNE_START " start"
+#define STR_PID_BAD_HEATER_ID " failed! Bad heater id"
+#define STR_PID_TEMP_TOO_HIGH " failed! Temperature too high"
+#define STR_PID_TIMEOUT " failed! timeout"
+#define STR_BIAS " bias: "
+#define STR_D_COLON " d: "
+#define STR_T_MIN " min: "
+#define STR_T_MAX " max: "
+#define STR_KU " Ku: "
+#define STR_TU " Tu: "
+#define STR_CLASSIC_PID " Classic PID "
+#define STR_KP " Kp: "
+#define STR_KI " Ki: "
+#define STR_KD " Kd: "
+#define STR_PID_AUTOTUNE_FINISHED " finished! Put the last Kp, Ki and Kd constants from below into Configuration.h"
+#define STR_PID_DEBUG " PID_DEBUG "
+#define STR_PID_DEBUG_INPUT ": Input "
+#define STR_PID_DEBUG_OUTPUT " Output "
+#define STR_INVALID_EXTRUDER_NUM " - Invalid extruder number !"
+#define STR_MPC_AUTOTUNE "MPC Autotune"
+#define STR_MPC_AUTOTUNE_START " start for " STR_E
+#define STR_MPC_AUTOTUNE_INTERRUPTED " interrupted!"
+#define STR_MPC_AUTOTUNE_FINISHED " finished! Put the constants below into Configuration.h"
+#define STR_MPC_COOLING_TO_AMBIENT "Cooling to ambient"
+#define STR_MPC_HEATING_PAST_200 "Heating to over 200C"
+#define STR_MPC_MEASURING_AMBIENT "Measuring ambient heatloss at "
+#define STR_MPC_TEMPERATURE_ERROR "Temperature error"
+
+#define STR_HEATER_BED "bed"
+#define STR_HEATER_CHAMBER "chamber"
+#define STR_COOLER "cooler"
+#define STR_MOTHERBOARD "motherboard"
+#define STR_PROBE "probe"
+#define STR_REDUNDANT "redundant "
+#define STR_LASER_TEMP "laser temperature"
+
+#define STR_STOPPED_HEATER ", system stopped! Heater_ID: "
+#define STR_REDUNDANCY "Heater switched off. Temperature difference between temp sensors is too high !"
+#define STR_T_HEATING_FAILED "Heating failed"
+#define STR_T_THERMAL_RUNAWAY "Thermal Runaway"
+#define STR_T_MALFUNCTION "Thermal Malfunction"
+#define STR_T_MAXTEMP "MAXTEMP triggered"
+#define STR_T_MINTEMP "MINTEMP triggered"
+#define STR_ERR_PROBING_FAILED "Probing Failed"
+#define STR_ZPROBE_OUT_SER "Z Probe Past Bed"
+
+// Debug
+#define STR_DEBUG_PREFIX "DEBUG:"
+#define STR_DEBUG_OFF "off"
+#define STR_DEBUG_ECHO "ECHO"
+#define STR_DEBUG_INFO "INFO"
+#define STR_DEBUG_ERRORS "ERRORS"
+#define STR_DEBUG_DRYRUN "DRYRUN"
+#define STR_DEBUG_COMMUNICATION "COMMUNICATION"
+#define STR_DEBUG_DETAIL "DETAIL"
+
+#define STR_PRINTER_LOCKED "Printer locked! (Unlock with M511 or LCD)"
+#define STR_WRONG_PASSWORD "Incorrect Password"
+#define STR_PASSWORD_TOO_LONG "Password too long"
+#define STR_PASSWORD_REMOVED "Password removed"
+#define STR_REMINDER_SAVE_SETTINGS "Remember to save!"
+#define STR_PASSWORD_SET "Password is "
+
+// Settings Report Strings
+#define STR_Z_AUTO_ALIGN "Z Auto-Align"
+#define STR_BACKLASH_COMPENSATION "Backlash compensation"
+#define STR_S_SEG_PER_SEC "S"
+#define STR_DELTA_SETTINGS "Delta (L R H S XYZ ABC)"
+#define STR_SCARA_SETTINGS "SCARA"
+#define STR_POLARGRAPH_SETTINGS "Polargraph"
+#define STR_SCARA_P_T_Z "P T Z"
+#define STR_ENDSTOP_ADJUSTMENT "Endstop adjustment"
+#define STR_SKEW_FACTOR "Skew Factor"
+#define STR_FILAMENT_SETTINGS "Filament settings"
+#define STR_MAX_ACCELERATION "Max Acceleration (units/s2)"
+#define STR_MAX_FEEDRATES "Max feedrates (units/s)"
+#define STR_ACCELERATION_P_R_T "Acceleration (units/s2) (P R T)"
+#define STR_TOOL_CHANGING "Tool-changing"
+#define STR_HOTEND_OFFSETS "Hotend offsets"
+#define STR_SERVO_ANGLES "Servo Angles"
+#define STR_HOTEND_PID "Hotend PID"
+#define STR_BED_PID "Bed PID"
+#define STR_CHAMBER_PID "Chamber PID"
+#define STR_STEPS_PER_UNIT "Steps per unit"
+#define STR_LINEAR_ADVANCE "Linear Advance"
+#define STR_CONTROLLER_FAN "Controller Fan"
+#define STR_STEPPER_MOTOR_CURRENTS "Stepper motor currents"
+#define STR_RETRACT_S_F_Z "Retract (S F Z)"
+#define STR_RECOVER_S_F "Recover (S F)"
+#define STR_AUTO_RETRACT_S "Auto-Retract (S)"
+#define STR_FILAMENT_LOAD_UNLOAD "Filament load/unload"
+#define STR_POWER_LOSS_RECOVERY "Power-loss recovery"
+#define STR_FILAMENT_RUNOUT_SENSOR "Filament runout sensor"
+#define STR_DRIVER_STEPPING_MODE "Driver stepping mode"
+#define STR_STEPPER_DRIVER_CURRENT "Stepper driver current"
+#define STR_HYBRID_THRESHOLD "Hybrid Threshold"
+#define STR_STALLGUARD_THRESHOLD "StallGuard threshold"
+#define STR_HOME_OFFSET "Home offset"
+#define STR_SOFT_ENDSTOPS "Soft endstops"
+#define STR_MATERIAL_HEATUP "Material heatup parameters"
+#define STR_LCD_CONTRAST "LCD Contrast"
+#define STR_LCD_BRIGHTNESS "LCD Brightness"
+#define STR_DISPLAY_SLEEP "Display Sleep"
+#define STR_UI_LANGUAGE "UI Language"
+#define STR_Z_PROBE_OFFSET "Z-Probe Offset"
+#define STR_TEMPERATURE_UNITS "Temperature Units"
+#define STR_USER_THERMISTORS "User thermistors"
+#define STR_DELAYED_POWEROFF "Delayed poweroff"
+
+//
+// Endstop Names used by Endstops::report_states
+//
+#define STR_X_MIN "x_min"
+#define STR_X_MAX "x_max"
+#define STR_X2_MIN "x2_min"
+#define STR_X2_MAX "x2_max"
+
+#if HAS_Y_AXIS
+ #define STR_Y_MIN "y_min"
+ #define STR_Y_MAX "y_max"
+ #define STR_Y2_MIN "y2_min"
+ #define STR_Y2_MAX "y2_max"
+#endif
+
+#if HAS_Z_AXIS
+ #define STR_Z_MIN "z_min"
+ #define STR_Z_MAX "z_max"
+ #define STR_Z2_MIN "z2_min"
+ #define STR_Z2_MAX "z2_max"
+ #define STR_Z3_MIN "z3_min"
+ #define STR_Z3_MAX "z3_max"
+ #define STR_Z4_MIN "z4_min"
+ #define STR_Z4_MAX "z4_max"
+#endif
+
+#define STR_Z_PROBE "z_probe"
+#define STR_PROBE_EN "probe_en"
+#define STR_FILAMENT "filament"
+
+// General axis names
+#define STR_X "X"
+#define STR_Y "Y"
+#define STR_Z "Z"
+#define STR_E "E"
+#if IS_KINEMATIC
+ #define STR_A "A"
+ #define STR_B "B"
+ #define STR_C "C"
+#else
+ #define STR_A "X"
+ #define STR_B "Y"
+ #define STR_C "Z"
+#endif
+#define STR_X2 "X2"
+#define STR_Y2 "Y2"
+#define STR_Z2 "Z2"
+#define STR_Z3 "Z3"
+#define STR_Z4 "Z4"
+
+// Extra Axis and Endstop Names
+#if HAS_I_AXIS
+ #if AXIS4_NAME == 'A'
+ #define STR_I "A"
+ #define STR_I_MIN "a_min"
+ #define STR_I_MAX "a_max"
+ #elif AXIS4_NAME == 'B'
+ #define STR_I "B"
+ #define STR_I_MIN "b_min"
+ #define STR_I_MAX "b_max"
+ #elif AXIS4_NAME == 'C'
+ #define STR_I "C"
+ #define STR_I_MIN "c_min"
+ #define STR_I_MAX "c_max"
+ #elif AXIS4_NAME == 'U'
+ #define STR_I "U"
+ #define STR_I_MIN "u_min"
+ #define STR_I_MAX "u_max"
+ #elif AXIS4_NAME == 'V'
+ #define STR_I "V"
+ #define STR_I_MIN "v_min"
+ #define STR_I_MAX "v_max"
+ #elif AXIS4_NAME == 'W'
+ #define STR_I "W"
+ #define STR_I_MIN "w_min"
+ #define STR_I_MAX "w_max"
+ #else
+ #error "AXIS4_NAME can only be one of 'A', 'B', 'C', 'U', 'V', or 'W'."
+ #endif
+#else
+ #define STR_I ""
+#endif
+
+#if HAS_J_AXIS
+ #if AXIS5_NAME == 'B'
+ #define STR_J "B"
+ #define STR_J_MIN "b_min"
+ #define STR_J_MAX "b_max"
+ #elif AXIS5_NAME == 'C'
+ #define STR_J "C"
+ #define STR_J_MIN "c_min"
+ #define STR_J_MAX "c_max"
+ #elif AXIS5_NAME == 'U'
+ #define STR_J "U"
+ #define STR_J_MIN "u_min"
+ #define STR_J_MAX "u_max"
+ #elif AXIS5_NAME == 'V'
+ #define STR_J "V"
+ #define STR_J_MIN "v_min"
+ #define STR_J_MAX "v_max"
+ #elif AXIS5_NAME == 'W'
+ #define STR_J "W"
+ #define STR_J_MIN "w_min"
+ #define STR_J_MAX "w_max"
+ #else
+ #error "AXIS5_NAME can only be one of 'B', 'C', 'U', 'V', or 'W'."
+ #endif
+#else
+ #define STR_J ""
+#endif
+
+#if HAS_K_AXIS
+ #if AXIS6_NAME == 'C'
+ #define STR_K "C"
+ #define STR_K_MIN "c_min"
+ #define STR_K_MAX "c_max"
+ #elif AXIS6_NAME == 'U'
+ #define STR_K "U"
+ #define STR_K_MIN "u_min"
+ #define STR_K_MAX "u_max"
+ #elif AXIS6_NAME == 'V'
+ #define STR_K "V"
+ #define STR_K_MIN "v_min"
+ #define STR_K_MAX "v_max"
+ #elif AXIS6_NAME == 'W'
+ #define STR_K "W"
+ #define STR_K_MIN "w_min"
+ #define STR_K_MAX "w_max"
+ #else
+ #error "AXIS6_NAME can only be one of 'C', 'U', 'V', or 'W'."
+ #endif
+#else
+ #define STR_K ""
+#endif
+
+#if HAS_U_AXIS
+ #if AXIS7_NAME == 'U'
+ #define STR_U "U"
+ #define STR_U_MIN "u_min"
+ #define STR_U_MAX "u_max"
+ #elif AXIS7_NAME == 'V'
+ #define STR_U "V"
+ #define STR_U_MIN "v_min"
+ #define STR_U_MAX "v_max"
+ #elif AXIS7_NAME == 'W'
+ #define STR_U "W"
+ #define STR_U_MIN "w_min"
+ #define STR_U_MAX "w_max"
+ #else
+ #error "AXIS7_NAME can only be one of 'U', 'V', or 'W'."
+ #endif
+#else
+ #define STR_U ""
+#endif
+
+#if HAS_V_AXIS
+ #if AXIS8_NAME == 'V'
+ #define STR_V "V"
+ #define STR_V_MIN "v_min"
+ #define STR_V_MAX "v_max"
+ #elif AXIS8_NAME == 'W'
+ #define STR_V "W"
+ #define STR_V_MIN "w_min"
+ #define STR_V_MAX "w_max"
+ #else
+ #error "AXIS8_NAME can only be one of 'V', or 'W'."
+ #endif
+#else
+ #define STR_V ""
+#endif
+
+#if HAS_W_AXIS
+ #if AXIS9_NAME == 'W'
+ #define STR_W "W"
+ #define STR_W_MIN "w_min"
+ #define STR_W_MAX "w_max"
+ #else
+ #error "AXIS9_NAME can only be 'W'."
+ #endif
+#else
+ #define STR_W ""
+#endif
+
+#if EITHER(HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL)
+
+ // Custom characters defined in the first 8 characters of the LCD
+ #define LCD_STR_BEDTEMP "\x00" // Print only as a char. This will have 'unexpected' results when used in a string!
+ #define LCD_STR_DEGREE "\x01"
+ #define LCD_STR_THERMOMETER "\x02" // Still used with string concatenation
+ #define LCD_STR_UPLEVEL "\x03"
+ #define LCD_STR_REFRESH "\x04"
+ #define LCD_STR_FOLDER "\x05"
+ #define LCD_STR_FEEDRATE "\x06"
+ #define LCD_STR_CLOCK "\x07"
+ #define LCD_STR_ARROW_RIGHT ">" /* from the default character set */
+
+#else
+ //
+ // Custom characters from Marlin_symbols.fon which was merged into ISO10646-0-3.bdf
+ // \x00 intentionally skipped to avoid problems in strings
+ //
+ #define LCD_STR_REFRESH "\x01"
+ #define LCD_STR_FOLDER "\x02"
+ #define LCD_STR_ARROW_RIGHT "\x03"
+ #define LCD_STR_UPLEVEL "\x04"
+ #define LCD_STR_CLOCK "\x05"
+ #define LCD_STR_FEEDRATE "\x06"
+ #define LCD_STR_BEDTEMP "\x07"
+ #define LCD_STR_THERMOMETER "\x08"
+ #define LCD_STR_DEGREE "\x09"
+
+ #define LCD_STR_SPECIAL_MAX '\x09'
+ // Maximum here is 0x1F because 0x20 is ' ' (space) and the normal charsets begin.
+ // Better stay below 0x10 because DISPLAY_CHARSET_HD44780_WESTERN begins here.
+
+ // Symbol characters
+ #define LCD_STR_FILAM_DIA "\xF8"
+ #define LCD_STR_FILAM_MUL "\xA4"
+
+#endif
+
+/**
+ * Tool indexes for LCD display only
+ *
+ * By convention the LCD shows "E1" for the first extruder.
+ * However, internal to Marlin E0/T0 is the first tool, and
+ * most board silkscreens say "E0." Zero-based labels will
+ * make these indexes consistent but this defies expectation.
+ */
+#if ENABLED(NUMBER_TOOLS_FROM_0)
+ #define LCD_FIRST_TOOL 0
+ #define STR_N0 "0"
+ #define STR_N1 "1"
+ #define STR_N2 "2"
+ #define STR_N3 "3"
+ #define STR_N4 "4"
+ #define STR_N5 "5"
+ #define STR_N6 "6"
+ #define STR_N7 "7"
+#else
+ #define LCD_FIRST_TOOL 1
+ #define STR_N0 "1"
+ #define STR_N1 "2"
+ #define STR_N2 "3"
+ #define STR_N3 "4"
+ #define STR_N4 "5"
+ #define STR_N5 "6"
+ #define STR_N6 "7"
+ #define STR_N7 "8"
+#endif
+
+#define STR_E0 STR_E STR_N0
+#define STR_E1 STR_E STR_N1
+#define STR_E2 STR_E STR_N2
+#define STR_E3 STR_E STR_N3
+#define STR_E4 STR_E STR_N4
+#define STR_E5 STR_E STR_N5
+#define STR_E6 STR_E STR_N6
+#define STR_E7 STR_E STR_N7
+
+// Include localized LCD Menu Messages
+
+#define LANGUAGE_DATA_INCL_(M) STRINGIFY_(fontdata/langdata_##M.h)
+#define LANGUAGE_DATA_INCL(M) LANGUAGE_DATA_INCL_(M)
+
+#define LANGUAGE_INCL_(M) STRINGIFY_(../lcd/language/language_##M.h)
+#define LANGUAGE_INCL(M) LANGUAGE_INCL_(M)
+
+// Use superscripts, if possible. Evaluated at point of use.
+#define SUPERSCRIPT_TWO TERN(NOT_EXTENDED_ISO10646_1_5X7, "^2", "²")
+#define SUPERSCRIPT_THREE TERN(NOT_EXTENDED_ISO10646_1_5X7, "^3", "³")
+
+#include "multi_language.h" // Allow multiple languages
+
+#include "../lcd/language/language_en.h"
+#include LANGUAGE_INCL(LCD_LANGUAGE)
+#include LANGUAGE_INCL(LCD_LANGUAGE_2)
+#include LANGUAGE_INCL(LCD_LANGUAGE_3)
+#include LANGUAGE_INCL(LCD_LANGUAGE_4)
+#include LANGUAGE_INCL(LCD_LANGUAGE_5)
+
+#if NONE(DISPLAY_CHARSET_ISO10646_1, \
+ DISPLAY_CHARSET_ISO10646_5, \
+ DISPLAY_CHARSET_ISO10646_KANA, \
+ DISPLAY_CHARSET_ISO10646_GREEK, \
+ DISPLAY_CHARSET_ISO10646_CN, \
+ DISPLAY_CHARSET_ISO10646_TR, \
+ DISPLAY_CHARSET_ISO10646_PL, \
+ DISPLAY_CHARSET_ISO10646_CZ, \
+ DISPLAY_CHARSET_ISO10646_SK)
+ #define DISPLAY_CHARSET_ISO10646_1 // use the better font on full graphic displays.
+#endif
diff --git a/Marlin/src/core/macros.h b/Marlin/src/core/macros.h
new file mode 100644
index 0000000000..fec390b64b
--- /dev/null
+++ b/Marlin/src/core/macros.h
@@ -0,0 +1,770 @@
+/**
+ * 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 .
+ *
+ */
+#pragma once
+
+#ifndef __has_include
+ #define __has_include(...) 1
+#endif
+
+#define ABCE 4
+#define XYZE 4
+#define ABC 3
+#define XYZ 3
+#define XY 2
+
+#define _AXIS(A) (A##_AXIS)
+
+#define _XSTOP_ 0x01
+#define _YSTOP_ 0x02
+#define _ZSTOP_ 0x03
+#define _ISTOP_ 0x04
+#define _JSTOP_ 0x05
+#define _KSTOP_ 0x06
+#define _USTOP_ 0x07
+#define _VSTOP_ 0x08
+#define _WSTOP_ 0x09
+#define _XMIN_ 0x11
+#define _YMIN_ 0x12
+#define _ZMIN_ 0x13
+#define _IMIN_ 0x14
+#define _JMIN_ 0x15
+#define _KMIN_ 0x16
+#define _UMIN_ 0x17
+#define _VMIN_ 0x18
+#define _WMIN_ 0x19
+#define _XMAX_ 0x21
+#define _YMAX_ 0x22
+#define _ZMAX_ 0x23
+#define _IMAX_ 0x24
+#define _JMAX_ 0x25
+#define _KMAX_ 0x26
+#define _UMAX_ 0x27
+#define _VMAX_ 0x28
+#define _WMAX_ 0x29
+#define _XDIAG_ 0x31
+#define _YDIAG_ 0x32
+#define _ZDIAG_ 0x33
+#define _IDIAG_ 0x34
+#define _JDIAG_ 0x35
+#define _KDIAG_ 0x36
+#define _UDIAG_ 0x37
+#define _VDIAG_ 0x38
+#define _WDIAG_ 0x39
+#define _E0DIAG_ 0xE0
+#define _E1DIAG_ 0xE1
+#define _E2DIAG_ 0xE2
+#define _E3DIAG_ 0xE3
+#define _E4DIAG_ 0xE4
+#define _E5DIAG_ 0xE5
+#define _E6DIAG_ 0xE6
+#define _E7DIAG_ 0xE7
+
+#define _FORCE_INLINE_ __attribute__((__always_inline__)) __inline__
+#define FORCE_INLINE __attribute__((always_inline)) inline
+#define NO_INLINE __attribute__((noinline))
+#define _UNUSED __attribute__((unused))
+#define __O0 __attribute__((optimize("O0")))
+#define __Os __attribute__((optimize("Os")))
+#define __O1 __attribute__((optimize("O1")))
+#define __O2 __attribute__((optimize("O2")))
+#define __O3 __attribute__((optimize("O3")))
+
+#define IS_CONSTEXPR(...) __builtin_constant_p(__VA_ARGS__) // Only valid solution with C++14. Should use std::is_constant_evaluated() in C++20 instead
+
+#ifndef UNUSED
+ #define UNUSED(x) ((void)(x))
+#endif
+
+// Clock speed factors
+#if !defined(CYCLES_PER_MICROSECOND) && !defined(__STM32F1__)
+ #define CYCLES_PER_MICROSECOND (F_CPU / 1000000UL) // 16 or 20 on AVR
+#endif
+
+// Nanoseconds per cycle
+#define NANOSECONDS_PER_CYCLE (1000000000.0 / F_CPU)
+
+// Macros to make a string from a macro
+#define STRINGIFY_(M) #M
+#define STRINGIFY(M) STRINGIFY_(M)
+
+#define A(CODE) " " CODE "\n\t"
+#define L(CODE) CODE ":\n\t"
+
+// Macros for bit masks
+#undef _BV
+#define _BV(n) (1<<(n))
+#define TEST(n,b) (!!((n)&_BV(b)))
+#define SET_BIT_TO(N,B,TF) do{ if (TF) SBI(N,B); else CBI(N,B); }while(0)
+#ifndef SBI
+ #define SBI(A,B) (A |= _BV(B))
+#endif
+#ifndef CBI
+ #define CBI(A,B) (A &= ~_BV(B))
+#endif
+#define TBI(N,B) (N ^= _BV(B))
+#define _BV32(b) (1UL << (b))
+#define TEST32(n,b) !!((n)&_BV32(b))
+#define SBI32(n,b) (n |= _BV32(b))
+#define CBI32(n,b) (n &= ~_BV32(b))
+#define TBI32(N,B) (N ^= _BV32(B))
+
+#define cu(x) ({__typeof__(x) _x = (x); (_x)*(_x)*(_x);})
+#define RADIANS(d) ((d)*float(M_PI)/180.0f)
+#define DEGREES(r) ((r)*180.0f/float(M_PI))
+#define HYPOT2(x,y) (sq(x)+sq(y))
+#define NORMSQ(x,y,z) (sq(x)+sq(y)+sq(z))
+
+#define CIRCLE_AREA(R) (float(M_PI) * sq(float(R)))
+#define CIRCLE_CIRC(R) (2 * float(M_PI) * float(R))
+
+#define SIGN(a) ({__typeof__(a) _a = (a); (_a>0)-(_a<0);})
+#define IS_POWER_OF_2(x) ((x) && !((x) & ((x) - 1)))
+
+// Macros to constrain values
+#ifdef __cplusplus
+
+ // C++11 solution that is standards compliant.
+ template static constexpr void NOLESS(V& v, const N n) {
+ if (n > v) v = n;
+ }
+ template static constexpr void NOMORE(V& v, const N n) {
+ if (n < v) v = n;
+ }
+ template static constexpr void LIMIT(V& v, const N1 n1, const N2 n2) {
+ if (n1 > v) v = n1;
+ else if (n2 < v) v = n2;
+ }
+
+#else
+
+ #define NOLESS(v, n) \
+ do{ \
+ __typeof__(v) _n = (n); \
+ if (_n > v) v = _n; \
+ }while(0)
+
+ #define NOMORE(v, n) \
+ do{ \
+ __typeof__(v) _n = (n); \
+ if (_n < v) v = _n; \
+ }while(0)
+
+ #define LIMIT(v, n1, n2) \
+ do{ \
+ __typeof__(v) _n1 = (n1); \
+ __typeof__(v) _n2 = (n2); \
+ if (_n1 > v) v = _n1; \
+ else if (_n2 < v) v = _n2; \
+ }while(0)
+
+#endif
+
+// Macros to chain up to 40 conditions
+#define _DO_1(W,C,A) (_##W##_1(A))
+#define _DO_2(W,C,A,B) (_##W##_1(A) C _##W##_1(B))
+#define _DO_3(W,C,A,V...) (_##W##_1(A) C _DO_2(W,C,V))
+#define _DO_4(W,C,A,V...) (_##W##_1(A) C _DO_3(W,C,V))
+#define _DO_5(W,C,A,V...) (_##W##_1(A) C _DO_4(W,C,V))
+#define _DO_6(W,C,A,V...) (_##W##_1(A) C _DO_5(W,C,V))
+#define _DO_7(W,C,A,V...) (_##W##_1(A) C _DO_6(W,C,V))
+#define _DO_8(W,C,A,V...) (_##W##_1(A) C _DO_7(W,C,V))
+#define _DO_9(W,C,A,V...) (_##W##_1(A) C _DO_8(W,C,V))
+#define _DO_10(W,C,A,V...) (_##W##_1(A) C _DO_9(W,C,V))
+#define _DO_11(W,C,A,V...) (_##W##_1(A) C _DO_10(W,C,V))
+#define _DO_12(W,C,A,V...) (_##W##_1(A) C _DO_11(W,C,V))
+#define _DO_13(W,C,A,V...) (_##W##_1(A) C _DO_12(W,C,V))
+#define _DO_14(W,C,A,V...) (_##W##_1(A) C _DO_13(W,C,V))
+#define _DO_15(W,C,A,V...) (_##W##_1(A) C _DO_14(W,C,V))
+#define _DO_16(W,C,A,V...) (_##W##_1(A) C _DO_15(W,C,V))
+#define _DO_17(W,C,A,V...) (_##W##_1(A) C _DO_16(W,C,V))
+#define _DO_18(W,C,A,V...) (_##W##_1(A) C _DO_17(W,C,V))
+#define _DO_19(W,C,A,V...) (_##W##_1(A) C _DO_18(W,C,V))
+#define _DO_20(W,C,A,V...) (_##W##_1(A) C _DO_19(W,C,V))
+#define _DO_21(W,C,A,V...) (_##W##_1(A) C _DO_20(W,C,V))
+#define _DO_22(W,C,A,V...) (_##W##_1(A) C _DO_21(W,C,V))
+#define _DO_23(W,C,A,V...) (_##W##_1(A) C _DO_22(W,C,V))
+#define _DO_24(W,C,A,V...) (_##W##_1(A) C _DO_23(W,C,V))
+#define _DO_25(W,C,A,V...) (_##W##_1(A) C _DO_24(W,C,V))
+#define _DO_26(W,C,A,V...) (_##W##_1(A) C _DO_25(W,C,V))
+#define _DO_27(W,C,A,V...) (_##W##_1(A) C _DO_26(W,C,V))
+#define _DO_28(W,C,A,V...) (_##W##_1(A) C _DO_27(W,C,V))
+#define _DO_29(W,C,A,V...) (_##W##_1(A) C _DO_28(W,C,V))
+#define _DO_30(W,C,A,V...) (_##W##_1(A) C _DO_29(W,C,V))
+#define _DO_31(W,C,A,V...) (_##W##_1(A) C _DO_30(W,C,V))
+#define _DO_32(W,C,A,V...) (_##W##_1(A) C _DO_31(W,C,V))
+#define _DO_33(W,C,A,V...) (_##W##_1(A) C _DO_32(W,C,V))
+#define _DO_34(W,C,A,V...) (_##W##_1(A) C _DO_33(W,C,V))
+#define _DO_35(W,C,A,V...) (_##W##_1(A) C _DO_34(W,C,V))
+#define _DO_36(W,C,A,V...) (_##W##_1(A) C _DO_35(W,C,V))
+#define _DO_37(W,C,A,V...) (_##W##_1(A) C _DO_36(W,C,V))
+#define _DO_38(W,C,A,V...) (_##W##_1(A) C _DO_37(W,C,V))
+#define _DO_39(W,C,A,V...) (_##W##_1(A) C _DO_38(W,C,V))
+#define _DO_40(W,C,A,V...) (_##W##_1(A) C _DO_39(W,C,V))
+#define __DO_N(W,C,N,V...) _DO_##N(W,C,V)
+#define _DO_N(W,C,N,V...) __DO_N(W,C,N,V)
+#define DO(W,C,V...) (_DO_N(W,C,NUM_ARGS(V),V))
+
+// Macros to support option testing
+#define _CAT(a,V...) a##V
+#define CAT(a,V...) _CAT(a,V)
+
+#define _ISENA_ ~,1
+#define _ISENA_1 ~,1
+#define _ISENA_0x1 ~,1
+#define _ISENA_true ~,1
+#define _ISENA(V...) IS_PROBE(V)
+
+#define _ENA_1(O) _ISENA(CAT(_IS,CAT(ENA_, O)))
+#define _DIS_1(O) NOT(_ENA_1(O))
+#define ENABLED(V...) DO(ENA,&&,V)
+#define DISABLED(V...) DO(DIS,&&,V)
+#define COUNT_ENABLED(V...) DO(ENA,+,V)
+
+#define TERN(O,A,B) _TERN(_ENA_1(O),B,A) // OPTION ? 'A' : 'B'
+#define TERN0(O,A) _TERN(_ENA_1(O),0,A) // OPTION ? 'A' : '0'
+#define TERN1(O,A) _TERN(_ENA_1(O),1,A) // OPTION ? 'A' : '1'
+#define TERN_(O,A) _TERN(_ENA_1(O),,A) // OPTION ? 'A' : ''
+#define _TERN(E,V...) __TERN(_CAT(T_,E),V) // Prepend 'T_' to get 'T_0' or 'T_1'
+#define __TERN(T,V...) ___TERN(_CAT(_NO,T),V) // Prepend '_NO' to get '_NOT_0' or '_NOT_1'
+#define ___TERN(P,V...) THIRD(P,V) // If first argument has a comma, A. Else B.
+
+#define _OPTITEM(A...) A,
+#define OPTITEM(O,A...) TERN_(O,DEFER4(_OPTITEM)(A))
+#define _OPTARG(A...) , A
+#define OPTARG(O,A...) TERN_(O,DEFER4(_OPTARG)(A))
+#define _OPTCODE(A) A;
+#define OPTCODE(O,A) TERN_(O,DEFER4(_OPTCODE)(A))
+
+// Macros to avoid 'f + 0.0' which is not always optimized away. Minus included for symmetry.
+// Compiler flags -fno-signed-zeros -ffinite-math-only also cover 'f * 1.0', 'f - f', etc.
+#define PLUS_TERN0(O,A) _TERN(_ENA_1(O),,+ (A)) // OPTION ? '+ (A)' : ''
+#define MINUS_TERN0(O,A) _TERN(_ENA_1(O),,- (A)) // OPTION ? '- (A)' : ''
+#define SUM_TERN(O,B,A) ((B) PLUS_TERN0(O,A)) // ((B) (OPTION ? '+ (A)' : ''))
+#define DIFF_TERN(O,B,A) ((B) MINUS_TERN0(O,A)) // ((B) (OPTION ? '- (A)' : ''))
+
+#define IF_ENABLED TERN_
+#define IF_DISABLED(O,A) TERN(O,,A)
+
+#define ANY(V...) !DISABLED(V)
+#define NONE(V...) DISABLED(V)
+#define ALL(V...) ENABLED(V)
+#define BOTH(V1,V2) ALL(V1,V2)
+#define EITHER(V1,V2) ANY(V1,V2)
+#define MANY(V...) (COUNT_ENABLED(V) > 1)
+
+// Macros to support pins/buttons exist testing
+#define PIN_EXISTS(PN) (defined(PN##_PIN) && PN##_PIN >= 0)
+#define _PINEX_1 PIN_EXISTS
+#define PINS_EXIST(V...) DO(PINEX,&&,V)
+#define ANY_PIN(V...) DO(PINEX,||,V)
+
+#define BUTTON_EXISTS(BN) (defined(BTN_##BN) && BTN_##BN >= 0)
+#define _BTNEX_1 BUTTON_EXISTS
+#define BUTTONS_EXIST(V...) DO(BTNEX,&&,V)
+#define ANY_BUTTON(V...) DO(BTNEX,||,V)
+
+#define WITHIN(N,L,H) ((N) >= (L) && (N) <= (H))
+#define ISEOL(C) ((C) == '\n' || (C) == '\r')
+#define NUMERIC(a) WITHIN(a, '0', '9')
+#define DECIMAL(a) (NUMERIC(a) || a == '.')
+#define HEXCHR(a) (NUMERIC(a) ? (a) - '0' : WITHIN(a, 'a', 'f') ? ((a) - 'a' + 10) : WITHIN(a, 'A', 'F') ? ((a) - 'A' + 10) : -1)
+#define NUMERIC_SIGNED(a) (NUMERIC(a) || (a) == '-' || (a) == '+')
+#define DECIMAL_SIGNED(a) (DECIMAL(a) || (a) == '-' || (a) == '+')
+#define COUNT(a) (sizeof(a)/sizeof(*a))
+#define ZERO(a) memset((void*)a,0,sizeof(a))
+#define COPY(a,b) do{ \
+ static_assert(sizeof(a[0]) == sizeof(b[0]), "COPY: '" STRINGIFY(a) "' and '" STRINGIFY(b) "' types (sizes) don't match!"); \
+ memcpy(&a[0],&b[0],_MIN(sizeof(a),sizeof(b))); \
+ }while(0)
+
+#define CODE_16( A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,...) A; B; C; D; E; F; G; H; I; J; K; L; M; N; O; P
+#define CODE_15( A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,...) A; B; C; D; E; F; G; H; I; J; K; L; M; N; O
+#define CODE_14( A,B,C,D,E,F,G,H,I,J,K,L,M,N,...) A; B; C; D; E; F; G; H; I; J; K; L; M; N
+#define CODE_13( A,B,C,D,E,F,G,H,I,J,K,L,M,...) A; B; C; D; E; F; G; H; I; J; K; L; M
+#define CODE_12( A,B,C,D,E,F,G,H,I,J,K,L,...) A; B; C; D; E; F; G; H; I; J; K; L
+#define CODE_11( A,B,C,D,E,F,G,H,I,J,K,...) A; B; C; D; E; F; G; H; I; J; K
+#define CODE_10( A,B,C,D,E,F,G,H,I,J,...) A; B; C; D; E; F; G; H; I; J
+#define CODE_9( A,B,C,D,E,F,G,H,I,...) A; B; C; D; E; F; G; H; I
+#define CODE_8( A,B,C,D,E,F,G,H,...) A; B; C; D; E; F; G; H
+#define CODE_7( A,B,C,D,E,F,G,...) A; B; C; D; E; F; G
+#define CODE_6( A,B,C,D,E,F,...) A; B; C; D; E; F
+#define CODE_5( A,B,C,D,E,...) A; B; C; D; E
+#define CODE_4( A,B,C,D,...) A; B; C; D
+#define CODE_3( A,B,C,...) A; B; C
+#define CODE_2( A,B,...) A; B
+#define CODE_1( A,...) A
+#define CODE_0(...)
+#define _CODE_N(N,V...) CODE_##N(V)
+#define CODE_N(N,V...) _CODE_N(N,V)
+
+#define GANG_16(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,...) A B C D E F G H I J K L M N O P
+#define GANG_15(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,...) A B C D E F G H I J K L M N O
+#define GANG_14(A,B,C,D,E,F,G,H,I,J,K,L,M,N,...) A B C D E F G H I J K L M N
+#define GANG_13(A,B,C,D,E,F,G,H,I,J,K,L,M...) A B C D E F G H I J K L M
+#define GANG_12(A,B,C,D,E,F,G,H,I,J,K,L...) A B C D E F G H I J K L
+#define GANG_11(A,B,C,D,E,F,G,H,I,J,K,...) A B C D E F G H I J K
+#define GANG_10(A,B,C,D,E,F,G,H,I,J,...) A B C D E F G H I J
+#define GANG_9( A,B,C,D,E,F,G,H,I,...) A B C D E F G H I
+#define GANG_8( A,B,C,D,E,F,G,H,...) A B C D E F G H
+#define GANG_7( A,B,C,D,E,F,G,...) A B C D E F G
+#define GANG_6( A,B,C,D,E,F,...) A B C D E F
+#define GANG_5( A,B,C,D,E,...) A B C D E
+#define GANG_4( A,B,C,D,...) A B C D
+#define GANG_3( A,B,C,...) A B C
+#define GANG_2( A,B,...) A B
+#define GANG_1( A,...) A
+#define GANG_0(...)
+#define _GANG_N(N,V...) GANG_##N(V)
+#define GANG_N(N,V...) _GANG_N(N,V)
+#define GANG_N_1(N,K) _GANG_N(N,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K)
+
+// Macros for initializing arrays
+#define LIST_26(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z
+#define LIST_25(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y
+#define LIST_24(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X
+#define LIST_23(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W
+#define LIST_22(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V
+#define LIST_21(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U
+#define LIST_20(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T
+#define LIST_19(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S
+#define LIST_18(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R
+#define LIST_17(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q
+#define LIST_16(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P
+#define LIST_15(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O
+#define LIST_14(A,B,C,D,E,F,G,H,I,J,K,L,M,N,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N
+#define LIST_13(A,B,C,D,E,F,G,H,I,J,K,L,M,...) A,B,C,D,E,F,G,H,I,J,K,L,M
+#define LIST_12(A,B,C,D,E,F,G,H,I,J,K,L,...) A,B,C,D,E,F,G,H,I,J,K,L
+#define LIST_11(A,B,C,D,E,F,G,H,I,J,K,...) A,B,C,D,E,F,G,H,I,J,K
+#define LIST_10(A,B,C,D,E,F,G,H,I,J,...) A,B,C,D,E,F,G,H,I,J
+#define LIST_9( A,B,C,D,E,F,G,H,I,...) A,B,C,D,E,F,G,H,I
+#define LIST_8( A,B,C,D,E,F,G,H,...) A,B,C,D,E,F,G,H
+#define LIST_7( A,B,C,D,E,F,G,...) A,B,C,D,E,F,G
+#define LIST_6( A,B,C,D,E,F,...) A,B,C,D,E,F
+#define LIST_5( A,B,C,D,E,...) A,B,C,D,E
+#define LIST_4( A,B,C,D,...) A,B,C,D
+#define LIST_3( A,B,C,...) A,B,C
+#define LIST_2( A,B,...) A,B
+#define LIST_1( A,...) A
+#define LIST_0(...)
+
+#define _LIST_N(N,V...) LIST_##N(V)
+#define LIST_N(N,V...) _LIST_N(N,V)
+#define LIST_N_1(N,K) _LIST_N(N,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K)
+#define ARRAY_N(N,V...) { _LIST_N(N,V) }
+#define ARRAY_N_1(N,K) { LIST_N_1(N,K) }
+
+#define _JOIN_1(O) (O)
+#define JOIN_N(N,C,V...) (DO(JOIN,C,LIST_N(N,V)))
+
+#define LOOP_S_LE_N(VAR, S, N) for (uint8_t VAR=(S); VAR<=(N); VAR++)
+#define LOOP_S_L_N(VAR, S, N) for (uint8_t VAR=(S); VAR<(N); VAR++)
+#define LOOP_LE_N(VAR, N) LOOP_S_LE_N(VAR, 0, N)
+#define LOOP_L_N(VAR, N) LOOP_S_L_N(VAR, 0, N)
+
+#define NOOP (void(0))
+
+#define CEILING(x,y) (((x) + (y) - 1) / (y))
+
+#undef ABS
+#ifdef __cplusplus
+ template static constexpr const T ABS(const T v) { return v >= 0 ? v : -v; }
+#else
+ #define ABS(a) ({__typeof__(a) _a = (a); _a >= 0 ? _a : -_a;})
+#endif
+
+#define UNEAR_ZERO(x) ((x) < 0.000001f)
+#define NEAR_ZERO(x) WITHIN(x, -0.000001f, 0.000001f)
+#define NEAR(x,y) NEAR_ZERO((x)-(y))
+
+#define RECIPROCAL(x) (NEAR_ZERO(x) ? 0 : (1 / float(x)))
+#define FIXFLOAT(f) ({__typeof__(f) _f = (f); _f + (_f < 0 ? -0.0000005f : 0.0000005f);})
+
+//
+// Maths macros that can be overridden by HAL
+//
+#define ACOS(x) acosf(x)
+#define ATAN2(y, x) atan2f(y, x)
+#define POW(x, y) powf(x, y)
+#define SQRT(x) sqrtf(x)
+#define RSQRT(x) (1.0f / sqrtf(x))
+#define CEIL(x) ceilf(x)
+#define FLOOR(x) floorf(x)
+#define TRUNC(x) truncf(x)
+#define LROUND(x) lroundf(x)
+#define FMOD(x, y) fmodf(x, y)
+#define HYPOT(x,y) SQRT(HYPOT2(x,y))
+
+// Use NUM_ARGS(__VA_ARGS__) to get the number of variadic arguments
+#define _NUM_ARGS(_,n,m,l,k,j,i,h,g,f,e,d,c,b,a,Z,Y,X,W,V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A,OUT,...) OUT
+#define NUM_ARGS(V...) _NUM_ARGS(0,V,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
+
+// Use TWO_ARGS(__VA_ARGS__) to get whether there are 1, 2, or >2 arguments
+#define _TWO_ARGS(_,n,m,l,k,j,i,h,g,f,e,d,c,b,a,Z,Y,X,W,V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A,OUT,...) OUT
+#define TWO_ARGS(V...) _TWO_ARGS(0,V,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1,0)
+
+#ifdef __cplusplus
+
+ #ifndef _MINMAX_H_
+ #define _MINMAX_H_
+
+ extern "C++" {
+
+ // C++11 solution that is standards compliant. Return type is deduced automatically
+ template static constexpr auto _MIN(const L lhs, const R rhs) -> decltype(lhs + rhs) {
+ return lhs < rhs ? lhs : rhs;
+ }
+ template static constexpr auto _MAX(const L lhs, const R rhs) -> decltype(lhs + rhs) {
+ return lhs > rhs ? lhs : rhs;
+ }
+ template static constexpr const T _MIN(T V, Ts... Vs) { return _MIN(V, _MIN(Vs...)); }
+ template static constexpr const T _MAX(T V, Ts... Vs) { return _MAX(V, _MAX(Vs...)); }
+
+ }
+
+ #endif
+
+ // Allow manipulating enumeration value like flags without ugly cast everywhere
+ #define ENUM_FLAGS(T) \
+ FORCE_INLINE constexpr T operator&(T x, T y) { return static_cast(static_cast(x) & static_cast(y)); } \
+ FORCE_INLINE constexpr T operator|(T x, T y) { return static_cast(static_cast(x) | static_cast(y)); } \
+ FORCE_INLINE constexpr T operator^(T x, T y) { return static_cast(static_cast(x) ^ static_cast(y)); } \
+ FORCE_INLINE constexpr T operator~(T x) { return static_cast(~static_cast(x)); } \
+ FORCE_INLINE T & operator&=(T &x, T y) { return x &= y; } \
+ FORCE_INLINE T & operator|=(T &x, T y) { return x |= y; } \
+ FORCE_INLINE T & operator^=(T &x, T y) { return x ^= y; }
+
+ // C++11 solution that is standard compliant. is not available on all platform
+ namespace Private {
+ template struct enable_if { };
+ template