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 (build_all_bootloaders.sh) 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 http://openocd.org/doc/doxygen/bugs.html 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:
Trying 127.0.0.1... 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.
init 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:
load_image fw.bin 0x00000000 bin
> 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.