Marlin 2.0 using Makefile (and reclaiming io-pins on atmega1284p based mainboard)

I am using Marlin 2.0 to control my 3D printers, I like to be in full control so I compile my own firmware, adding my own tweaks. Most people probably use the Arduino IDE to compile and install the firmware – I am more to vi and Makefile

This post will cover how I set up my system so I can compile Marlin for multiple mainboards , while sharing the same source-code-tree, the boards/printers covered here are:

  • MKS GEN v1.3 using atmega2560 as used on Tevo Tarantula
  • Melzi 2.0 using atmega1284p as used on Tronxy X1

Marlin has a Makefile which with a little tweaking can compile the firmware, but there is no provision to have a common source-tree to compile for different boards/cpu’s. Let us change that.

The goal is to have a subdir per board which contains the configuration files, and object-files used during compilation. Here is how I would compile new firmware for the two printers :

$ cd Marlin-2.0.x-git/Marlin/
$ ls -l . Tarantula Tronxy_X1
-rw-r--r--  1 peter peter 124938 Mar 19 21:32 Configuration_adv.h
-rw-r--r--  1 peter peter  82430 Mar 19 21:34 Configuration.h
drwxr-xr-x  2 peter peter      3 Feb 27  2019 lib
-rw-r--r--  1 peter peter  30323 Mar 20 11:39 Makefile
-rw-r--r--  1 peter peter   1922 Mar 11 19:12 Marlin.ino
drwxr-xr-x 12 peter peter     15 Mar 19 21:31 src
drwxr-xr-x  3 peter peter      7 Mar 20 13:12 Tarantula
drwxr-xr-x  2 peter peter      5 Mar 20 13:13 Tronxy_X1
-rw-r--r--  1 peter peter   2535 Mar 11 19:12 Version.h

Tarantula:
-rw-r--r-- 1 peter peter 124225 Mar 20 11:34 Configuration_adv.h
-rw-r--r-- 1 peter peter  97284 Mar 20 12:19 Configuration.h
-rw-r--r-- 1 peter peter   1272 Mar 20 12:52 Makefile

Tronxy_X1:
-rw-r--r-- 1 peter peter 124934 Mar 19 20:12 Configuration_adv.h
-rw-r--r-- 1 peter peter  84251 Mar 20 09:59 Configuration.h
-rw-r--r-- 1 peter peter   1614 Mar 20 12:53 Makefile
$ cd Tarantula; make
...
   text    data     bss     dec     hex filename
 162226     370    4331  166927   28c0f objs/Tarantula.elf

$ cd ../Tronxy_X1; make
...

   text    data     bss     dec     hex filename
 129920     396    3835  134151   20c07 objs/Tronxy_X1.elf

The per board Makefiles I created are quite small, it includes the standard Makefile which contains the bulk of the rules, The Configuration files typically comes from github.com/MarlinFirmware/Configurations/archive/bugfix-2.0.x.zip and the Marlin source is from github.com/MarlinFirmware/Marlin

Makefile for Tevo Tarantula

# Makefile for Tevo Tarantula 
 ARDUINO_INSTALL_DIR  = $(HOME)/Arduino-1.8.12-x86_64
 HARDWARE_MOTHERBOARD = 1110  # MKS v1.3
 MCU                  = atmega2560
 HARDWARE_VARIANT     = arduino
 VPATH += .
 VPATH += ..
 VPATH += objs
 SRC_DIR         = ../src
 BUILD_DIR       = objs
 # Note no space allowed before comment #
 LIQUID_CRYSTAL ?= 1# DEFAULT 6 io-pins directly control
 U8GLIB         ?= 0# we use a character LCD
 TMC            ?= 0# not using Trinamic TMCStepper
 AVRDUDE_CONF    = /etc/avrdude.conf
 UPLOAD_RATE     = 115200
 include ../Makefile

Makefile for Tronxy X1, LCD via i2c

For my Tronxy X1 the Makefile is a bit more complicated, mostly because I have modded the hardware, The Tronxy X1 is using an atmeaga1284p as CPU, the little sister to the atmega2560 with less pins and half as much FLASH. I needed an extra io-pin for a filament sensor, but there is no unused inputs, so I converted the LCD to be accessed via i2c, which uses 2 io-pins instead of 6. Marlin has support to use a 20×4 character lcd connected via i2c and a pcf8574 so the change is not complicated, but FLASH is limited, so I had to replace the i2c-driver with a leaner one.

# Makefile for Tronxy X1
# with LCD via I2C
 ARDUINO_INSTALL_DIR  = $(HOME)/Arduino-1.8.12-x86_64
 HARDWARE_MOTHERBOARD = 1502  # Melzi
 MCU                  = atmega1284p
 HARDWARE_VARIANT     = Sanguino
 HARDWARE_SUB_VARIANT = sanguino
 VPATH += .
 VPATH += ..
 VPATH += objs
 MightyCore = $(HOME)/MightyCore
 VPATH += $(MightyCore)/avr/cores/MCUdude_corefiles
 VPATH += $(MightyCore)/avr/variants/sanguino 
 VPATH += $(MightyCore)/avr/libraries/SPI/src
 SRC_DIR      = ../src
 BUILD_DIR    = objs
 # Note no space allowed before comment #
 LIQUID_CRYSTAL          ?= 0# 6 io-pins directly controlling the LCD
 LIQUID_CRYSTAL_I2C      ?= 0# LCD connected vi i2c pcf8574
 LIQUID_CRYSTAL_I2C_TINY ?= 1# as above saves text=642 data=18 bss=182
 U8GLIB                  ?= 0# we use character LCD
 TMC                     ?= 0# not using Trinamic TMCStepper
 ifeq ($(LIQUID_CRYSTAL_I2C), 1)
   VPATH += $(MightyCore)/avr/libraries/Wire/src
   VPATH += $(MightyCore)/avr/libraries/Wire/src/utility
   VPATH += $(HOME)/Arduino/libraries/LiquidCrystal_I2C
   LIB_SRC += twi.c
   LIB_CXXSRC += Wire.cpp
   LIB_CXXSRC += LiquidCrystal_I2C.cpp
 endif
 ifeq ($(LIQUID_CRYSTAL_I2C_TINY), 1)
   VPATH += $(HOME)/Arduino/libraries/SoftI2CMaster
   VPATH += $(HOME)/Arduino/libraries/LiquidCrystal_I2C_Tiny
   LIB_CXXSRC += SoftI2CMaster.cpp
   LIB_CXXSRC += LiquidCrystal_I2C_Tiny.cpp
 endif
 AVRDUDE_CONF = /etc/avrdude.conf
 UPLOAD_RATE  = 115200
 include ../Makefile

Modifcation to Marlin Makefile

My Makefiles over-ride some of settings used in the standard Makefile which is included in the end. This requires a few trivial changes is in the Marlin Makefile, ?= means that variable will only be set if not set already, and += appends a setting instead of just overriding it, some #ifdef to opt out of using the standard LCD-library, and lastly SRC_DIR can be set to direct the Makefile where to find the source-files, see the diffs below:

diff --git a/Marlin/Makefile b/Marlin/Makefile
index fcd763881..4a295d5bc 100644
--- a/Marlin/Makefile
+++ b/Marlin/Makefile
@@ -80,6 +80,9 @@ UPLOAD_PORT        ?= /dev/ttyUSB0
 #on linux it is best to put an absolute path like /home/username/tmp .
 BUILD_DIR          ?= applet
 
+# This defines whether LiquidCrystal support will be built
+LIQUID_CRYSTAL     ?= 1
+
 # This defines whether Liquid_TWI2 support will be built
 LIQUID_TWI2        ?= 0
 
@@ -529,12 +532,14 @@ TARGET = $(notdir $(CURDIR))
 # source files, but for Marlin 2.0, we use VPATH only for arduino
 # library files.
 
-VPATH = .
+VPATH += .
 VPATH += $(BUILD_DIR)
 VPATH += $(HARDWARE_SRC)
 
 ifeq ($(HARDWARE_VARIANT), $(filter $(HARDWARE_VARIANT),arduino Teensy Sanguino))
+ifeq ($(LIQUID_CRYSTAL), 1)
 VPATH += $(ARDUINO_INSTALL_DIR)/hardware/marlin/avr/libraries/LiquidCrystal/src
+endif
 VPATH += $(ARDUINO_INSTALL_DIR)/hardware/marlin/avr/libraries/SPI
 endif
 
@@ -546,7 +551,9 @@ ifeq ($(IS_MCU),1)
   VPATH += $(ARDUINO_INSTALL_DIR)/hardware/arduino/avr/libraries/SoftwareSerial/src
 endif
 
+ifeq ($(LIQUID_CRYSTAL), 1)
 VPATH += $(ARDUINO_INSTALL_DIR)/libraries/LiquidCrystal/src
+endif
 ifeq ($(LIQUID_TWI2), 1)
 VPATH += $(ARDUINO_INSTALL_DIR)/libraries/Wire
 VPATH += $(ARDUINO_INSTALL_DIR)/libraries/Wire/utility
@@ -593,7 +600,7 @@ else
   VPATH += $(ARDUINO_INSTALL_DIR)/hardware/$(HARDWARE_VARIANT)/variants/$(HARDWARE_SUB_VARIANT)
 endif
 
-LIB_SRC = wiring.c \
+LIB_SRC += wiring.c \
   wiring_analog.c wiring_digital.c \
   wiring_shift.c WInterrupts.c hooks.c
 
@@ -604,19 +611,21 @@ else
 endif
 
 ifeq ($(HARDWARE_VARIANT), Teensy)
-  LIB_SRC = wiring.c
+  LIB_SRC += wiring.c
   VPATH += $(ARDUINO_INSTALL_DIR)/hardware/teensy/cores/teensy
 endif
 
-LIB_CXXSRC = WMath.cpp WString.cpp Print.cpp SPI.cpp
+LIB_CXXSRC += WMath.cpp WString.cpp Print.cpp SPI.cpp
 
 ifeq ($(NEOPIXEL), 1)
   LIB_CXXSRC += Adafruit_NeoPixel.cpp
 endif
 
-ifeq ($(LIQUID_TWI2), 0)
+ifeq ($(LIQUID_CRYSTAL), 1)
   LIB_CXXSRC += LiquidCrystal.cpp
-else
+endif
+
+ifeq ($(LIQUID_TWI2), 1)
   LIB_SRC += twi.c
   LIB_CXXSRC += Wire.cpp LiquidTWI2.cpp
 endif
@@ -708,7 +717,7 @@ CXXWARN = -Wall                     -Wno-packed-bitfield-compat -Wno-pragmas -Wu
 CTUNING = -fsigned-char -funsigned-bitfields -fno-exceptions \
           -fshort-enums -ffunction-sections -fdata-sections
 ifneq ($(HARDWARE_MOTHERBOARD),)
-  CTUNING += -DMOTHERBOARD=${HARDWARE_MOTHERBOARD}
+#  CTUNING += -DMOTHERBOARD=${HARDWARE_MOTHERBOARD}
 endif
 #CEXTRA = -Wa,-adhlns=$(<:.c=.lst)
 CXXEXTRA = -fno-use-cxa-atexit -fno-threadsafe-statics -fno-rtti
@@ -730,9 +739,9 @@ endif
 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
+  AVRDUDE_CONF ?= /etc/avrdude/avrdude.conf
 else
-  AVRDUDE_CONF = $(ARDUINO_INSTALL_DIR)/hardware/tools/avr/etc/avrdude.conf
+  AVRDUDE_CONF ?= $(ARDUINO_INSTALL_DIR)/hardware/tools/avr/etc/avrdude.conf
 endif
 AVRDUDE_FLAGS = -D -C$(AVRDUDE_CONF) \
   -p$(MCU) -P$(AVRDUDE_PORT) -c$(AVRDUDE_PROGRAMMER) \
@@ -741,15 +750,17 @@ AVRDUDE_FLAGS = -D -C$(AVRDUDE_CONF) \
 # 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)
+SRC_DIR ?= src
+
+SRC    = $(shell find $(SRC_DIR) -name '*.c'   -type f)
+CXXSRC = $(shell find $(SRC_DIR) -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}}
+OBJ += ${patsubst $(SRC_DIR)/%.c,   $(BUILD_DIR)/src/%.o, ${SRC}}
+OBJ += ${patsubst $(SRC_DIR)/%.cpp, $(BUILD_DIR)/src/%.o, ${CXXSRC}}
 
 # Define all listing files.
 LST = $(LIB_ASRC:.S=.lst) $(LIB_CXXSRC:.cpp=.lst) $(LIB_SRC:.c=.lst)

One of the include files also need to be modified to be able to find the configuration files in their new position.

diff --git a/Marlin/src/inc/MarlinConfigPre.h b/Marlin/src/inc/MarlinConfigPre.h
 index 1385f9e19..9a5d69e77 100644
 --- a/Marlin/src/inc/MarlinConfigPre.h
 +++ b/Marlin/src/inc/MarlinConfigPre.h
 @@ -34,7 +34,8 @@
  #include "../core/boards.h"
  #include "../core/macros.h"
 -#include "../../Configuration.h"
 +//#include "../../Configuration.h"
 +#include "Configuration.h"
  #ifdef CUSTOM_VERSION_FILE
    #if defined(__has_include)
 @@ -52,7 +53,8 @@
  #include HAL_PATH(../HAL, inc/Conditionals_LCD.h)
  #include "../core/drivers.h"
 -#include "../../Configuration_adv.h"
 +//#include "../../Configuration_adv.h"
 +#include "Configuration_adv.h"
  #include "Conditionals_adv.h"
  #include HAL_PATH(../HAL, inc/Conditionals_adv.h)

These changes should not change Marlins ability to be compiled under the Arduino IDE.

Changes to let Tronxy X1 access its LCD via i2c

The Tronxy X1 uses a Melzi 2.0 mainboard and a 20×4 LCD with 5 buttons which is read via one analog input. I want to change the output to the LCD to go via i2c, the i2c-LCD-interface boards are available from Aliexpress for next to nothing. and takes over the control of the LCD, I just unsoldered the LCD, drilled out the holes in the current LCD/buttton-pcb, and had the connections run through to the new i2c-LCD board, scl/ada happens to be part of io-pins going to the board already, so these together with VCC  and GND are connected.  The buttons works as usual.  Check the photo above.

The Tronxy X1 Configuration file just need a number of #define, and two files in the Marlin distribution needs an #ifdef, the diffs are below

peter@t470:Marlin> git diff src/lcd/HD44780
diff --git a/Marlin/src/lcd/HD44780/ultralcd_HD44780.cpp b/Marlin/src/lcd/HD44780/ultralcd_HD44780.cpp
index a032450ad..c71c09823 100644
--- a/Marlin/src/lcd/HD44780/ultralcd_HD44780.cpp
+++ b/Marlin/src/lcd/HD44780/ultralcd_HD44780.cpp
@@ -68,6 +68,9 @@
 
 #elif ENABLED(LCD_I2C_TYPE_PCA8574)
 
+#ifdef LCD_I2C_TINY
+  SoftI2CMaster softi2c=SoftI2CMaster(PIN_WIRE_SCL,PIN_WIRE_SDA,0);
+#endif
   LCD_CLASS lcd(LCD_I2C_ADDRESS, LCD_WIDTH, LCD_HEIGHT);
 
 #elif ENABLED(SR_LCD_2W_NL)
@@ -354,7 +357,11 @@ void MarlinUI::init_lcd() {
     lcd.begin(LCD_WIDTH, LCD_HEIGHT);
 
   #elif ENABLED(LCD_I2C_TYPE_PCA8574)
+#ifdef LCD_I2C_TINY
+    lcd.begin();
+#else
     lcd.init();
+#endif
     lcd.backlight();
 
   #else
diff --git a/Marlin/src/lcd/HD44780/ultralcd_HD44780.h b/Marlin/src/lcd/HD44780/ultralcd_HD44780.h
index 12bf86a16..45b38fe54 100644
--- a/Marlin/src/lcd/HD44780/ultralcd_HD44780.h
+++ b/Marlin/src/lcd/HD44780/ultralcd_HD44780.h
@@ -74,7 +74,12 @@
   #define LCD_CLASS LiquidTWI2
 
 #elif ENABLED(LCD_I2C_TYPE_PCA8574)
+#ifdef LCD_I2C_TINY
+  #include 
+  #include 
+#else
   #include 
+#endif
   #define LCD_CLASS LiquidCrystal_I2C
 
 #elif ENABLED(SR_LCD_2W_NL)

The modification to Configuration.h, most of it is documentation.

--- Configurations-bugfix-2.0.x/config/examples/Tronxy/X1/Configuration.h	2020-03-10 15:59:48.000000000 -0400
+++ Configuration.h	2020-03-21 13:02:13.942851949 -0400
@@ -71,7 +71,62 @@
 // @section info
 
 // Author info of this build printed to the host during boot and M115
-#define STRING_CONFIG_H_AUTHOR "(Claus Naeveke, 0.1)" // Who made the changes.
+#define STRING_CONFIG_H_AUTHOR "(Peter Lorenzen, 0.1)" // Who made the changes.
+/*
+ * (Claus Naeveke 0.1) configured Marlin 2 for Tronxy X1, file found in
+ * https://github.com/MarlinFirmware/Configurations/archive/bugfix-2.0.x.zip
+ * file: Configurations-bugfix-2.0.x/config/examples/Tronxy/X1/Configuration.h
+ * I have made the folowing changes:
+ * BAUD-rate 250000
+ * extruder calibrated to geared extruder from Tevo Tarantula
+ * LCD changed to i2c to release pins for other use
+ * 10-pin 2x5 connector carries
+ *               +-----+
+ * (11) PC1 SDA -+1   2+- PA1 (30) buttons
+ * (10) PC0 SCL -+3   4+- PA2 (29) filament_sensor
+ * (17) PD3 TX1 -+5   6+- PA3 (28)
+ * (16) PD2 RX1 -+7   8+- PA4 (27)
+ *          VCC -+9  10+- GND (26)
+ *               +-----+
+ *
+ * SDA,SCL used to control LCD
+ * PA1 analog input to detect buttons
+ * LEFT  -| 470 |--+--| 4k7 |-- VCC
+ * RIGHT -| 4k7 |--+
+ * UP	 -|10k  |--+
+ * DOWN  -| 1k  |--+
+ * CENTER-| 2k2 |--+
+ * lcd schematic: https://reprap.org/forum/file.php?406,file=74922
+ * mainboard:     https://reprap.org/wiki/Melzi
+ *
+ * Arduino pin 16,17,27,28 still free
+ *
+ * Filament sensor: JST connector pint(1,2,3)=(Signal,Vcc,Gnd)
+ */
+#define ADVANCED_PAUSE_FEATURE
+
+// use the buttons as is but use i2c to control LCD free's 6 pins
+
+#define IS_ULTIPANEL
+#define LCD_I2C_TINY	// use SoftI@CMAster instead of wire
+#define LCD_I2C_TYPE_PCA8574
+#define LCD_I2C_ADDRESS 0x27   // I2C Address of the port expander
+#define LCD_WIDTH 20
+#define LCD_HEIGHT 4
+
+#define ADC_KEYPAD_PIN  1
+#define BTN_EN1        -1
+#define BTN_EN2        -1
+#define ADC_KEYPAD
+#define IS_RRW_KEYPAD
+#define REPRAPWORLD_KEYPAD_MOVE_STEP 10.0
+#define ADC_KEY_NUM 8
+  // This helps to implement ADC_KEYPAD menus
+#define REVERSE_MENU_DIRECTION
+#define ENCODER_PULSES_PER_STEP 1
+#define ENCODER_STEPS_PER_MENU_ITEM 1
+#define ENCODER_FEEDRATE_DEADZONE 2
+
 //#define CUSTOM_VERSION_FILE Version.h // Path from the root directory (no quotes)
 
 /**
@@ -1814,7 +1872,7 @@
 //
 // ANET and Tronxy 20x4 Controller
 //
-#define ZONESTAR_LCD              // Requires ADC_KEYPAD_PIN to be assigned to an analog pin.
+//#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.

And voila a controller board has been saved from going to the landfils

Print Friendly, PDF & Email
This entry was posted in 3D printer, Arduino. Bookmark the permalink.