Pull to refresh

STM32 without any trouble

Reading time12 min
Views1.9K

Easy Marlin installation on STM32 debug board

Confusion over bootloaders

Here’s a brief historical and technical overview to help us understand what a bootloader is, why you need it, and what it has to do with Marlin and STM32.

Arduino

Arduino boards use a special bootloader that allows you to update the microcontroller firmware without a programmer. This bootloader is not part of the microcontroller, but is pre-flashed into it by the board manufacturer and is never changed.

In 2009, leaflabs tried to produce its own line of Arduino-like boards based on STM32. A set of libmaple libraries and a special loader were developed for Arduino compatibility. This initiative did not have any notable success, and in 2015 the libmaple project was closed.

Marlin has never used or supported the bootloader from leaflabs.

STM32

STM32 microcontrollers have a built-in bootloader, which allows you to upload firmware without using a programmer or debugger. The bootloader is stored in the microcontroller’s non-volatile ROM and cannot be corrupted or deleted without physically damaging the chip.

A special BOOT0 pin is used to activate this bootloader. Depending on the signal applied to it at power-up or reboot, control is transferred either to custom firmware (low signal level) or to the built-in bootloader (high signal level).

The built-in bootloader allows one to upload the firmware using UART or USB connection. The latter option is available only on STM32F4 and newer microcontrollers.

Marlin

In December 2018, Marlin introduced support for the MKS Robin board with its proprietary bootloader, which allowed firmware updates from an encrypted file on the SD card. The MKS bootloader is stored at the beginning of the flash memory and from the microcontroller’s point of view is custom firmware. The firmware for MKS Robin had to be built using a non-standard location on the flash memory.

The ability to update the firmware from an SD card proved to be a decisive factor, and in spring 2019, BTT SKR Mini v1.1—the first commercial STM32-based 3D printer control board made specifically for Marlin 2.0—appeared on the market. Naturally, it had its own loader, but the general working principle did not change.

All STM32-based 3D printer control boards on the market use the manufacturer’s bootloader that allows you to update the firmware from an SD card. The size of such a loader depends on its functionality as well as the developer and varies from 8 to 64 kb. When you see mentions of a damaged bootloader, it’s all about this one.

In case of debug boards manufacturers don’t bother with a bootloader because it’s completely unnecessary. Flashing through the ST-Link debugger is infinitely easier, and if the developer needs a non-standard bootloader, he can flash it himself, and with exactly the right functionality.

If, for some reason, ST-Link is not available, there is always the possibility of flashing the debug board via the built-in bootloader.

Thus, when the word "bootloader" is used in relation to STM32, its meaning depends on the context. On a 3D printer control board, it’s a manufacturer’s bootloader, stored at the beginning of the flash memory, which allows updating the firmware from the SD card. On the debug board, it’s a built-in bootloader that uses its own ROM without taking up any space in flash memory.

Choosing a debug board

Any debug board can be used, but it’s worth considering that the availability of documentation saves you from having to manually create a pinout diagram, which is a huge time-saver.

Debug boards from STMicroelectronics Nucleo and Discovery series are well documented and come with a built-in ST-Link debugger. However, they cost more than their Chinese alternatives.

As a starting point for board selection, you can use the STM32-base project, which describes many popular debug boards on various STM32 microcontrollers.

I have two boards from this list lying around, so I will use them:

  • SM32F103ZET6 from EUse

  • SM32F407ZET6 from MCUDev

Connecting and uploading firmware

So, we’ve decided on types of bootloaders, and now let’s move on to the ways of uploading the firmware to the debug board.

The most convenient and versatile way is to use the ST-Link debugger. I won’t dwell on alternatives like J-Link, Black Magic Probe and CMSIS-DAP, because they are not as common as ST-Link. And if you have one of those, you already know how to work with it.

The second best in terms of convenience and simply the best in terms of economy is flashing via USB using DFU (device firmware update). It’s not as convenient as using ST-Link, because it requires rebooting the microcontroller and running the built-in bootloader, but it allows you to do without additional expenses for ST-Link or USB-UART adapter. Unfortunately, this method is not available to all and owners of F103-based debug boards have nothing to do but throw up their hands.

Another versatile way is to flash via UART using a USB-UART adapter (FT232R, CH340, etc.). It also requires rebooting the microcontroller and running the built-in bootloader, but it works for all STM32 controllers.

Some guides on how to flash STM32 mention such programs as ST-LINK Utility, Flash Loader Demonstrator and DfuSe used to upload the firmware via ST-Link, UART and USB. They are still available for download on the manufacturer’s site, but there you can notice that all three are no longer recommended for use and have been replaced by the universal STM32CubeProgrammer.

Download and install STM32CubeProgrammer, connect the debug board in the chosen way and check that the drivers for the USB device are installed. When connecting via UART and USB, do not forget to connect the BOOT0 pin to 3V3.

After that, start STM32CubeProgrammer, select the desired type of connection and press the Connect button. If you have not messed up the wires, then in a few seconds the program will inform you about a successful connection and show information about the controller used.

We don’t have ready firmware to upload yet, but now we know for sure that the necessary drivers are installed and the board is connected correctly.

Those who wish can make a copy of the demo firmware. To do this, in the Size field specify the size of the microcontroller flash memory (for STM32F103ZE it’s 524288 or 0x80000), click on the Read button and wait for the process to finish. Save the firmware by selecting Save As in the drop-down menu.

Reading the firmware from the microcontroller is not a quick process, especially via UART, so we won’t wait for those who want to make a backup copy. Let’s go straight to the Marlin assembly. As for STM32CubeProgrammer, we won’t need it anymore.

Marlin assembly

Now that the preparatory stage is over, we can move on to the fun part.

In the Marlin configuration there is a special type of board for working with non-standard hardware, which we’ll use. To do this, in Configuration.h enter:

#define MOTHERBOARD BOARD_CUSTOM

The pinout for this type of board is taken from the nonexistent Marlin\src\pins\pins_custom.h file. And this file does not exist to prevent it from being overwritten when updating the code.

Since we don’t have a real 3D printer, but a debug board, we need to define 4 pins of the microcontroller to create a minimal configuration:

  • The pin to which the LED is connected, which will blink to indicate that the firmware is successfully running.

  • The pin that will be used to read the state of all the end stops. I will use the one to which the custom buttons on the debug board are connected. That’s not necessary, but this way it will be possible to check the state of the end stops and demonstrate some working functionality.

  • The pin to which the control signals for the stepper motors, heaters and fan will be output is also one for all. In my case it will be the pin with the second diode.

  • The pin that will be used to read the temperature sensors, also one for all. Here the choice is quite limited, since it should be one of those pins that can work as an ADC input. Which pins can be used for this can be found in the microcontroller documentation as well as in the framework-arduinoststm32 code. For me it’s PA0.

STM32F103ZE and UART

Create a file Marlin\src\pins\pins_custom.h and fill it with the minimum necessary settings:

#define LED_PIN             PC0

#define DUMMY_IN            PF11

#define DUMMY_OUT           PD3

#define DUMMY_ADC           PA0

// Limit Switches

#define X_MIN_PIN           DUMMY_IN

#define Y_MIN_PIN           DUMMY_IN

#define Z_MIN_PIN           DUMMY_IN

// Steppers

#define X_STEP_PIN          DUMMY_OUT

#define X_DIR_PIN           DUMMY_OUT

#define X_ENABLE_PIN        DUMMY_OUT

#define Y_STEP_PIN          DUMMY_OUT

#define Y_DIR_PIN           DUMMY_OUT

#define Y_ENABLE_PIN        DUMMY_OUT

#define Z_STEP_PIN          DUMMY_OUT

#define Z_DIR_PIN           DUMMY_OUT

#define Z_ENABLE_PIN        DUMMY_OUT

#define E0_STEP_PIN         DUMMY_OUT

#define E0_DIR_PIN          DUMMY_OUT

#define E0_ENABLE_PIN       DUMMY_OUT

// Temperature Sensors

#define TEMP_0_PIN          DUMMY_ADC

#define TEMP_BED_PIN        DUMMY_ADC

// Heaters / Fans

#define HEATER_0_PIN        DUMMY_OUT

#define HEATER_BED_PIN      DUMMY_OUT

#define FAN0_PIN            DUMMY_OUT

To build firmware for BOARD_CUSTOM board we need to use a new environment [env:custom]. To do this, create an ini\custom.ini file and in the platformio.ini file in the [platformio] section add a new file to the extra_configs parameter. In the same section specify the environment you want to build for.

In the file ini\custom.ini create a custom environment for STM32F103ZE. There is no special description for the Euse M3 DEMO board, so we will use the basic version for this microcontroller. The whole range of available boards can be found in the .platformio\platforms\ststm32\boards catalog.

[env:custom]

extends           = common_stm32

board             = genericSTM32F103ZE

build_flags       = ${common_stm32.build_flags}

upload_protocol   = serial

This is the configuration for flashing via UART. If the flashing is done via ST-Link, it must be specified in the boot parameters:

upload_protocol   = stlink

There is no bootloader in the debug boards except for the built-in one. The firmware is written standardly, to the beginning of the flash memory, so no additional parameters are required. Flashing is always done via UART1 (PA9 and PA10), so we’ll specify the same port in Configuration.h:

#define SERIAL_PORT 1

There are no temperature sensors on the debug board, nor any wiring, and an attempt to read the temperature values from the ADC can produce excessively high values, which will lead to the firmware crash. To avoid this, let’s change the types of hotend and bed sensors to 998, which always reports a temperature of 25 ℃:

#define TEMP_SENSOR_0 998

#define TEMP_SENSOR_BED 998

We will also change the pull-up of the end stops, since the board description for the buttons says Active High:

#define ENDSTOPPULLDOWNS

If this is not done, pressing the button will not change the state of the end stops.

Now we can move on to building and uploading the firmware. Connect the BOOT0 pin to 3V3 using a jumper or the corresponding button and reboot the board. In PlatformIO press the Upload button on the status bar at the bottom of the screen and patiently wait for the process to finish.

The firmware is assembled and, judging by blinking, it works fine.

Next, let’s ground BOOT0, connect to the COM port with your favorite terminal and reboot the board. Marlin boots up and starts blinking again. Using the M119 command and the button on the board, check the functionality of the end stops.

Although nothing spectacular, everything works fine. Let’s add USB connectivity, SD card support and TFT screen, since the board has a separate slot for it. 

Let’s write this in Configuration.h:

#define SERIAL_PORT_2 -1

#define LCD_LANGUAGE ru

#define SDSUPPORT

#define TFT_GENERIC

#define TFT_INTERFACE_FSMC

#define TFT_RES_480x320

#define TFT_COLOR_UI

#define TFT_ROTATION TFT_ROTATE_270

#define TOUCH_SCREEN

It’s worth mentioning that using a virtual COM port (created when the board is connected via USB) is poorly suited for debugging firmware. Every time the microcontroller is rebooted, the connection drops, and most (or even all) terminal programs cannot reconnect automatically when the device is lost. The optimal option for debugging is an external USB-UART adapter, which will maintain a stable connection and avoid loss of firmware data.

Pins for connecting a display are determined by a thoughtful comparison of the inscriptions on the pinout of the board. SD card is connected to the SDIO interface of the microcontroller, which should also be specified in pins_custom.h:

#define TFT_CS_PIN          PG12

#define TFT_RS_PIN          PG0

#define TFT_BACKLIGHT_PIN   PB0

#define TFT_RESET_PIN       PC5

#define TOUCH_INT_PIN       PF10

#define TOUCH_CS_PIN        PB2

#define TOUCH_MISO_PIN      PF8

#define TOUCH_MOSI_PIN      PF9

#define TOUCH_SCK_PIN       PB1

#ifndef SDCARD_CONNECTION

  #define SDCARD_CONNECTION              ONBOARD

  #define ONBOARD_SDIO                            // Use SDIO for onboard SD

#endif

The framework-arduinoststm32 has a modular structure, and by default modules for SDIO and FSMC are not connected. You can connect them by adding two parameters to the assembly:

build_flags       = ${common_stm32.build_flags}

                    -DHAL_SD_MODULE_ENABLED

                    -DHAL_SRAM_MODULE_ENABLED

To avoid calibrating the touch screen after every reboot, let’s add the ability to save the settings:

#define EEPROM_SETTINGS

On the back of the Euse M3 DEMO board there is a place to install the EEPROM chip, but the chip itself is not soldered, so we’ll use flash memory of the controller to store settings. The resource of this flash memory is less than that of EEPROM (ten thousand write cycles against four million), but it’s quite enough for storing settings. For this purpose we’ll add the corresponding parameter in pins_custom.h:

#define FLASH_EEPROM_EMULATION

The final configuration for building under STM32F103ZE:

ini/custom.ini

[env:custom]

extends           = common_stm32

board             = genericSTM32F103ZE

build_flags       = ${common_stm32.build_flags}

                    -DHAL_SD_MODULE_ENABLED

                    -DHAL_SRAM_MODULE_ENABLED

upload_protocol   = serial

And here is pins_custom.h without the DUMMY part, which remains unchanged:

#pragma once

#define DEFAULT_MACHINE_NAME "STM32F103ZET6"

#define FLASH_EEPROM_EMULATION

#define LED_PIN             PD3

#define DUMMY_IN            PF11

#define DUMMY_OUT           PC0

#define DUMMY_ADC           PA0

#define TFT_CS_PIN          PG12

#define TFT_RS_PIN          PG0

#define TFT_BACKLIGHT_PIN   PB0

#define TFT_RESET_PIN       PC5

#define TOUCH_INT_PIN       PF10

#define TOUCH_CS_PIN        PB2

#define TOUCH_MISO_PIN      PF8

#define TOUCH_MOSI_PIN      PF9

#define TOUCH_SCK_PIN       PB1

// Onboard SD support

#ifndef SDCARD_CONNECTION

  #define SDCARD_CONNECTION              ONBOARD

  #define ONBOARD_SDIO                            // Use SDIO for onboard SD

#endif

STM32F407ZE and DFU

Now, let’s move on to building the firmware under STM32F407ZE. This board has its own description, which includes SDIO support, so you’ll need fewer parameters to build it.

[env:custom]

extends           = common_stm32

board             = black_f407ze

build_flags       = ${common_stm32.build_flags}

                    -DHAL_SRAM_MODULE_ENABLED

upload_protocol   = dfu

The buttons on the board work in the Active Low mode, and the screen for this board is smaller and differently arranged. In Configuration.h you need to change the corresponding parameters:

#define ENDSTOPPULLUPS

#define TFT_RES_320x240

#define TFT_ROTATION TFT_ROTATE_90

Also STM32F4 has 4 kb of RAM powered by the board battery. Marlin can use this memory to store settings, which is great for us:

#define SRAM_EEPROM_EMULATION

Here’s the final pins_custom.h without the DUMMY part, which remains unchanged:

#pragma once

#define DEFAULT_MACHINE_NAME "STM32F407ZET6"

#define SRAM_EEPROM_EMULATION

#define LED_PIN             PF10

#define DUMMY_IN            PE4

#define DUMMY_OUT           PF9

#define DUMMY_ADC           PA0

#define TFT_CS_PIN          PG12

#define TFT_RS_PIN          PF12

#define TFT_BACKLIGHT_PIN   PB15

#define TOUCH_INT_PIN       PB1

#define TOUCH_CS_PIN        PC13

#define TOUCH_MISO_PIN      PB2

#define TOUCH_MOSI_PIN      PF11

#define TOUCH_SCK_PIN       PB0

// Onboard SD support

#ifndef SDCARD_CONNECTION

  #define SDCARD_CONNECTION              ONBOARD

  #define ONBOARD_SDIO                            // Use SDIO for onboard SD

#endif

The next step is similar to the previous board: connect BOOT0 to 3V3, reboot and upload the firmware. As in that example, don’t forget to ground BOOT0 when there’s no need to update the firmware.

Flashing via ST-Link is a bit simpler, since it does not require manipulations with BOOT0 and manual reboot.

TL;DR

  • Marlin is easy and fast to install on STM32 debug boards.

  • PlatformIO can flash STM32 debug boards using ST-Link, UART or USB.

  • ST-Link is convenient, but not required. For STM32F4, a USB cable is enough.

This article was originally written by jmz, published at 3dtoday.ru, translated by the Top 3D Shop team.

Tags:
Hubs:
Total votes 2: ↑2 and ↓0+2
Comments0

Articles

Information

Website
top3dshop.com
Registered
Employees
11–30 employees