Compiling and flashing a bootloader on SAMD21 MCU using ST-Link V2

Some time ago I decided to build a Voron V2 3D printer. Mainly because my Tronxy X5S is not really reliable. I got Prusa MK3S clone, which is excellent, but it is hard to enclose to print ABS with it. The Voron is a beast of a machine with 4 independent Z steppers, for a total of 7 steppers on a machine with single extruder. This was a challenge as my PrntrBoard V2 could handle max of 6 stepper motors.

In the mean time I discovered that the Klipper firmware can run two boards, for example one with the 4 Z steppers and another with the A, B (Voron V2 is a CoreXY machine) and E steppers. That was cool, but a bit of a waste of electronic components. Then some pointed me to the HUVUD tool board a very nice small board designed to fit on the back of a NEMA 17 stepper motor. This board can control the extruder stepper, the hotend and the extruder fans.

There were two things I was not very fond of: first the board was using CAN interface and that would need extra wiring and USB->CAN adapter. The board was using STM32 MCU which is now quite rare and expensive due to the global chip shortage.

I set and re-designed the HUVUD board, got rig of the CAN bus and replaced it with USB control. Then I replaced the MCU with SAMD21E16 chip. Initially I tried E15, but the flash was not big enough.

Klipper seems to have support for this chip, there is Arduino Core and some cheap prototyping boards (Seeeduino Cortex M0+ – for example).

Finally to the topic at hand: how does one flash a bootloader onto SAMD21 MCU – which is quite handy for updating the firmware over USB. A quick google search would reveal several lengthy articles. The most useful one I found was from Adafruit. Alas this usually involves using a J-Link adapter and there are quite expensive. You can get clones from china, but they don’t work.

So here is my attempt to distill the information on the topic. First the bootloader code – the best place I found for this code is the Arduino Core for SAMD21 on github. In that repository there is a folder called bootloaders and inside there is a folder called “zero” – that contains the Arduino bootloader code.

There are excellent instructions in the README how to modify the code for your board – mainly define what pins your board uses for LED(s),  most of these are optional like the TX/RX LEDs. By default this code is for SAMD21x18 series MCU, but it works just fine on the versions with smaller flash. You can modify the bootloader_samd21x18.ld file and change the flash size to match your chip. Compiling the code is easy just use make with a parameter for your board. There is a shell script ( that compiles the code for a variety of boards – you can see how the parameters are defined there.

When you are done compiling the bootloader, by default you will have a .hex and a .bin file in the current folder. There is also a .elf file in the build/ folder, but we are not going to need that.

Connecting the debug adapter to the board: You need to connect the following signals from the ST-Link to your board (SWDIO, SWCLK, RST, GND). You can also power your board from the ST-LINK V2 adapter 3.3V supply, but that provides only minuscule amount of power.

You would need openocd to program the bootloader. I got the default version that comes with Ubuntu 20.04 (0.10.0), allegedly even version 0.9 would work, but YMMV.

Before we proceed you need to add 64 bytes of values 0xFF to your binary file with the bootloader code. Allegedly this is to work-around a bug in the openocd code. Without this padding you would get  errors in the verification step on the last page of the bootloader code. The value 0xff is the default value of the flash when the chip is erased.

To generate a file with 64 values of 0xff, I used the following command, courtesy of slashdot:

dd if=/dev/zero ibs=1 count=64 | tr "\000" "\377" >paddedFile.bin

Then just concatenated the two files together with

cat samd21_sam_ba_huvud_07s.bin paddedFile.bin>fw.bin

Here ‘samd21_sam_ba_huvud_07s.bin’ is the name of my compiled bootloader and paddedFile.bin is the file from the previous step.

Start openocd with the following magic command:

openocd -f interface/stlink-v2.cfg -c 'set CHIPNAME at91samd21e15; set ENDIAN little; set CPUTAPID 0x0bc11477; source [find target/at91samdXX.cfg]'

You may have to modify the MCU model (I used Samd21e15 chip, put your exact chip version there). You would see some console output similar to this:

Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate
adapter speed: 400 kHz
Info : Unable to match requested speed 400 kHz, using 240 kHz
Info : Unable to match requested speed 400 kHz, using 240 kHz
Info : clock speed 240 kHz
Info : STLINK v2 JTAG v31 API v2 SWIM v7 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.250331
Info : at91samd21e15.cpu: hardware has 4 breakpoints, 2 watchpoints

The program would appear to hang there, but no to worry, open another terminal window and type

telnet localhost 4444

This would open a telnet session to the openocd server. By default the openocd server listens for telnet connection on port 4444 and GDB connection on port 3333.

In the telnet terminal you would see something like:

Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger

This is the openocd command prompt and we are ready to send commands.

Send the following two commands first. Note that you would not get any response after the init command.

reset init

You would get a reponse after the ‘reset init’ command, some variation of the following:

target halted due to debug-request, current mode: Thread 
xPSR: 0x61000000 pc: 0x00000848 msp: 0x20000ffc

The pc, msp and xPSR register may hold different values for you.

You can erase the whole chip with the following command:

at91samd chip-erase
Note that this would erase all code stored on the chip, so proceed with caution.
Now we are ready to write the bootloader code. Send the following command:
load_image fw.bin 0x00000000 bin
Here fw.bin the the file we produced by concatenating the compiled bootloader code and the 64 byte padding file. The 0x00000000 is the start address in the flash where the code would be programmed and ‘bin’ is the file format.
Note the path name is relative to the folder where the openocd server was started, not the folder where the telned session is started. If you get an error “couldn’t open <path to>/fw.bin” it is most likely the openocd program can not open the path from its current location.
If all is well you would get something like the following response:
> load_image fw.bin 0x00000000 bin 
4924 bytes written at address 0x00000000
downloaded 4924 bytes in 0.291628s (16.489 KiB/s)

Now it is important to verify that the code was written to the flash correctly:

verify_image fw.bin 0x00000000 bin

Here again fw.bin the path to the firmware file and 0x00000000 is the start address in the flash where the code was written.

If all is well you would get something like the following response:

> verify_image fw.bin 0x00000000 bin
target halted due to breakpoint, current mode: Thread 
xPSR: 0x61000000 pc: 0x2000002e msp: 0x20000ffc
verified 4924 bytes in 0.459733s (10.460 KiB/s)

The time, number of bytes and speed will vary from system to system. If you get flash errors, for example:

target halted due to breakpoint, current mode: Thread 
xPSR: 0x61000000 pc: 0x2000002e msp: 0xfffffffc
Error: checksum mismatch - attempting binary compare
diff 0 address 0x000012c0. Was 0xff instead of 0x24
diff 1 address 0x000012c1. Was 0xff instead of 0x06
diff 2 address 0x000012c2. Was 0xff instead of 0x00
diff 3 address 0x000012c3. Was 0xff instead of 0x01

Verify that you erased the chip before attempting to program the code and you followed the padding instructions earlier. The example above was due to me not following the padding and I was always getting these errors in the last 58 bytes of the code.

To close the telnet session you can type ‘exit’, note that this would not exit the openocd server we started in the other window. You can connect to the server again with the telnet command.

PrntrBoard gets upgraded to 480MHz CPU!!!

I was perusing trough the parts catalog offered by JLCPCB for their assembly service and found this awesome micro controller  STM32H750VB it is quite an upgrade over the good old F407 part. It runs newer Cortex-M7 ARM core, it has double precision FP unit and can run at sweet 480MHz. Best of all it was mostly pin compatible with the F407. I had to re-route one side of the pins, but it was fairly quick.

The only down side is that the build-in flash is a bit limited – 128KB, but I think I can work with that. I’ll order some boards with that processor once the factories in China are bully back in business.

The updated design is on my github page in the “new_cpu” branch.

Happy 2020: PrntrBoardV2 update

I’ve been busy over the holidays. Here is a summary of the recent progress with the PrntrBoard V2 3D printer controller.

I decided to give the JLCPCB SMT Assembly service a try and ordered some TMC2209 driver boards as well as some semi-assembled PrntrBoard V2 controllers. Overall I was quite happy with the results.

Here is a picture of the driver boards. I tested all of them and they worked flawlessly out of the box.

These are the semi-assembled controllers. I use semi-assembled, because only the SMT components were populated and I have to spin my soldering robot to work on the thru hole connectors.

Next I tested the sensorless homing capability of the TMC2209 drivers. After some tuning of the sensitivity parameter is works very well. You can see one linear rail axis homing with no sensors in this video:

I also decided to work on improving the mounting of the drivers to the board. The driver boards were not very stable in the PCI Express slots and I designed these little plastic holders to fix them in place.

They are bolted to the bottom of the controller PCB. The drivers are very well affixed in place.

While I was trying to get my 2004 LCD panel to work with the controller, I found that not all “standard” EXP1 and EXP2 connectors have the same orientation. At first I though that the SKR 1.3 board, just has them backwards, but I then found this article on the RepRap discussion forum. It appears that the original schematics had an error and now some boards/displays are just backwards. I quickly flipped the connector on my 2004 display and with worked, but this can be quite frustrating.

I made a small PCB that one can use to flip the polarity of the connectors. “To flip or not to flip” seems to be ongoing question.

Progress update on PrntrBoard V2

I tried to do some thermal testing on the TMC2660 drivers. First I tried running my puny motor at 2A with a 60cm 5V fan blowing at the driver. This kept the driver quite cool, which was a not interesting.

I was planning to run all 3 axis at the same time, but my heat sink was just a bit too tall and was touching the other driver :-(. I ordered slightly shorter ones, but they have to swim trough the pacific ocean first.

While I was enjoying torturing the poor motor at it’s rated limit. The thermal camera picked an unusual hot spot on the board. The reverse protection diode was cooking quite a bit. Serves me right for being lazy and not doing the reverse protection the proper way. Even with only one motor running, the diode was dissipating quite a bit of heat. I can’t imagine in what world this part is rated for 10A continuous current. Maybe it needs a small refrigerator attached.

Anyway I changed the schematics to use a proper P-channel MOSFET and a zener.

I added a small cooling pad on the PCB to help the transistor cool off. Hopefully this will not overheat with full 10A load.

I also decided to test one of the last remaining circuits on the board – the servo connectors. This proved to be quite the challenge. There was a serious contention for Timer #7 on the micro controller.

First Marlin’s temperature control was set to use Timer 7. This was not making the Servo module happy. I moved the temperature module to use Timer 8. Then I found that the SoftwareSerial code was also trying to hijack Timer 7. Moved that to Timer 12.

Finally the servo was working properly. I was able to set the position with the M280 command. I decided to quit while I was ahead.

More next week.

PrntrBoard V2 status update

First my woes with the TMC2209 driver boards are ongoing. The company making the PCBs called that they could not complete my last two prototype designs. The issue was that I didn’t pay attention to the fabrication capabilities and used the wrong design rules.

Long story short I had to re-wire part of the board to meet their spec and submit another order. Alas that meant I have to wait another two weeks for the boards to appear 🙁

In the mean time I was trying to test what I can with the rev1 prototype I had. I tested the heaters and thermistors are working. Now it was turn to my old nemesis – the LCD panel.

The software for these LCD panels is remarkably convoluted and not at all supported on STM32 series of MCUs. I had to write two more drivers for the U8G library, but finally some good progress:

I got the FYSETC mini 12864 panel to work. To finish the week, I also verified the SD-card interface is working.

Here is a link to the github project with all the board design files. The Marlin firmware I’m using.

V2 prototype assembled – sort of

Here is my first assembled prototype of the V2 board. I only had patience to solder 3 PCIe connectors and skipped on the Thermocouple and servo connectors for now.

Sorry about the “no clean” solder paste gunk around the fuses. I noticed it after I took the picture.

Here is a picture with the driver boards in the slots

And slightly different angle

Here I added a 40x10mm fan for scale comparison

I’m not set on the 40mm fans for cooling, I ordered some 60mm and some 50mm and will do some experimentation what would be the best combination.

Next, I’ll make some driver boards and start porting Marlin to test the contraption.

Announcing PrntrBoard V2

Reflecting on the PrntrBoard V1, there are many good things that I managed to accomplish:

    • all 3 versions of the board (2130, 2660 and 2209) were functional
    • the board features were good
    • driver cooling was excellent

When I started 2 years ago, there were very few 32-bit boards with comparable features. Now there is quite a bit of them. I was trying to find what makes one design more popular than the other and in addition to the board features it comes down to flexibility.

In PrntrBoard V1 I was trying to provide superior cooling solution compared to the tiny replaceable driver board used everywhere. However that choice came at the expense of a monolithic design, which was expensive to make and costly to evolve. Every time I wanted to use a different driver chip I had to re-design the whole board from scratch. It was not going to be sustainable in the long run.

Announcing the PrntrBoardV2: combining all lessons learned and expanding the versatility of the design.

First major change is that the motor drivers are no longer part of the board. Because I still find the original Pololu driver form factor very limiting, I designed the motor driver carried boards to use PCIe slots instead of pin headers.

Here is what the carrier board looks like

The board has 32-bit Arm micro controller – my favorite STM32F407. There are 6 stepper motor slots, with support for 6 thermistors or 6 thermocouples (vie external boards).

There are 4 power MOSFET outputs (1 bed and 3 heaters) and 5 low power MOSFET outputs for fans or LEDs.

Connectivity is via traditional USB, micro-sd card. There are 2 more serial ports for a smart LCD controller and WiFi extension.

I have the traditional RAMPS LCD expansion headers, which should support a variety of LCD screen designs.

Last but not least there are 8 end stops and the ability to route the stall detection signal from the steppers to individual end stops or a global “Alarm” signal.

Drivers are on separate boards with PCIe card edge connectors. This is what the TMC2660 version looks like

And this is what the TMC2209 version looks like

I added some mounting holes next to the PCIe slots so the drivers would not wiggle out of the slot with extensive vibrations.

These driver boards are much larger than the Pololu drivers, which would allow for superior cooling and the ability to have more board real estate for complex designs and or big driver ICs.

In addition the driver boards are mounted vertically on the carrier board, which saves space on the carrier board and allow for excellent air flow trough the drivers with a pair of 40mm fans.

Last but not least because of the reduced requirements on the carrier board, I can use 2 layer instead of 4 layer board, which makes the cost even lower.

I can’t wait to make a few of these and run them trough some tests.

TMC2209 design thermal tests

I did some simple thermal tests on the TMC2209 board. In theory these driver chips can supply up to 2A RMS current to the motors. Silent StepSticks with the same drivers are rated around 1.2 to 1.4A depending on the manufacturer.

The test setup

I used all passive cooling. Ambient temperature was 25C. I used a small 9x9x12mm heatsink on top of the driver chip. These are commonly used on the TMC2209 StepStick boards from China (FYSETC or BIGTREETECH). These are not great, but that is what I could fit in the space. I’ll try to move some of the capacitors to make space for a larger heatsink.

On the bottom I used a 20x14x6 heatsink. Not ideal, but that is what I had laying around. There is enough space for a much larger one (25x25x8 for example).

I used a Seek thermal camera “mounted” on a small microscope stand and connected to my old Nexus 5X phone.

The results

First I tested it at 1A RMS current. I use several very slow speed motion commands (G1 Y250 F50 followed by G1 Y0). I found that slow speed motion is much more challenging to the driver heat  wise.

After about 20 minutes it board heated to about 47C on the top:

The heatsink was barely warm to the touch. In my experiments the top of the drivers always heats up more than the bottom, probably because of the relatively large thermal mass of the PCB itself.

Next I ran the board at 1.4A RMS. This was more challenging test for the heat dissipation. It took a while for the temperature to stop rising. It stabilized at around 64C on the top:

And around 55C on the bottom:

In the picture the heatsink looks “cool” because the camera can not compensate for the different emissivity of the bare aluminum.

Both top and bottom heatsinks were considerably hot to the touch. Not “burn your fingers” hot, but “I cant keep my fingers longer that 5 seconds” type of hot.

In both cases I did not get any over-temperature warnings from Marlin. I would say 1.4A is the limit on convection heat dissipation of this board in this configuration.

The Seek camera has around +/ – 5C accuracy.

I did one last test at 1.8A RMS. This was on the extreme side of the capabilities of the board. The temperature of the driver kept climbing slowly. Once it reached around 75C on the top I got an overheat alarm in Marlin, so I turned the power off.

I’m confident with some active cooling the driver would be able to run at this setting, because it took quite some time to get to the alarm.


Some early experiments with the TMC2209 board

Good news first: I managed to get one motor moving with a simple Arduino sketch.

I’m starting to hate QFN packages with a passion. I spent a whole afternoon trying to re-work two pesky drivers. The chips would not communicate via the UART port, no matter what I tried. Finally traced the issue to a bad solder joint on the QFN package and boy these are hard to spot. Simply re-heating the drivers and re-positioning was not enough, I had to remove the chip, add more solder paste, melt it, then re-insert the chip, wipe the excess solder with a soldering iron and finally re-flow the chip one more time. Complete and total PITA.

To top it off this destroys any near by plastic connectors, so now I have the drivers in-place but have to re-solder the connectors back. This would be an endeavor for the next week.

Now all 5 steppers are communicating with the MCU reliably. I had to add support for half-duplex mode to the stm32duino core. The proposed changes are still pending, but I verified that the TMCStepper library is able to communicate with the drivers.

First prototype of the TMC2209 design

This took me whole day. Working with QFN drivers is plain PITA. It does look good though. I just hope it works.

I finally figured out how to wash most of the flux from the board. It is still not perfect, but looks really good.

I have another revision with 3 fuses. I figured that one fuse for both motors and extruder heaters may be too taxing. In my latest design I have one 15A fuse for the heated bed; one 10A fuse for the extruder heaters and one more 10A fuse for the rest of the electronics.