Trying to build Firmware for STM32F070x6

I hope this is the right category for this issue.

I have been working at setting up the make menuconfig firmware process for the STM32F070x6. I have developed a small board using this MCU and I got samples in this week. This was discussed here: Klipper Firmware Fails to Start on Custom STM32F070 Board

The first order of business was to modify ~/klipper/src/stm32/Kconfig to support the MCU and I did that and I believe that it is producing the correct .config file for the Klipper Firmware Build. I made sure the System Memory address as well as the Flash/SRAM sizes and addresses are correct. You’ll see some changes in the Kconfig file to ensure the correct boot options are presented to the user.

2025.01.24-20.30 - Kconfig for stm32F070x6.txt (18.2 KB)

To try it out, rename it to simply Kconfig and load it into the ~/klipper/src/stm32/ directory. The changes that I made are marked with “#$#”.

Here is a sample of the .config created for the STM32F070x6:

stm32F070x6 - config from menuconfig - 2025.01.24-15.00 - Produced by rpi5.txt (2.9 KB)

Now, when running make, I got an error with spi.c - the STM32F070x6 only has one SPI port but the code looks explicitly for two. Here is the file with the changes (which start at line 62 and end at line 88):

spi.c.zip (1.8 KB)

When the Firmware is specified and built with the modified Kconfig and spi.c files, it is successful except for being noted that it is “Dirty”.

Unfortunately, when I try to load the firmware into the STM32F070x6 board, using DFU, I get:

$ sudo dfu-util -R -a 0 -s 0x08000000:force:mass-erase:leave -D ~/klipper/out/klipper.bin -d 0483:df11
dfu-util 0.11

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2021 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

dfu-util: Warning: Invalid DFU suffix signature
dfu-util: A valid DFU suffix will be required in a future dfu-util release
Opening DFU capable USB device...
Device ID 0483:df11
Device DFU version 011a
Claiming USB DFU Interface...
Setting Alternate Interface #0 ...
Determining device status...
DFU state(10) = dfuERROR, status(10) = Device's firmware is corrupt. It cannot return to run-time (non-DFU) operations
Clearing status
Determining device status...
DFU state(2) = dfuIDLE, status(0) = No error condition is present
DFU mode device DFU version 011a
Device returned transfer size 2048
DfuSe interface name: "Internal Flash  "
Performing mass erase, this can take a moment
Downloading element to address = 0x08000000, size = 31324
Erase           [=========================] 100%        31324 bytes
Erase    done.
Download        [=========================] 100%        31324 bytes
Download done.
File downloaded successfully
Submitting leave request...
Transitioning to dfuMANIFEST state
dfu-util: can't detach
Resetting USB to switch back to Run-Time mode
$

When I am expecting something like:

$ sudo dfu-util -R -a 0 -s 0x08000000:force:mass-erase:leave -D ~/klipper/out/klipper.bin -d 0483:df11
dfu-util 0.11

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2021 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

dfu-util: Warning: Invalid DFU suffix signature
dfu-util: A valid DFU suffix will be required in a future dfu-util release
Opening DFU capable USB device...
Device ID 0483:df11
Device DFU version 011a
Claiming USB DFU Interface...
Setting Alternate Interface #0 ...
Determining device status...
DFU state(2) = dfuIDLE, status(0) = No error condition is present
DFU mode device DFU version 011a
Device returned transfer size 1024
DfuSe interface name: "Internal Flash   "
Performing mass erase, this can take a moment
Downloading element to address = 0x08000000, size = 37668
Erase           [=========================] 100%        37668 bytes
Erase    done.
Download        [=========================] 100%        37668 bytes
Download done.
File downloaded successfully
Submitting leave request...
dfu-util: Error during download get_status
$

A board programmed with the Firmware and having communication set to USB is not recognized by ls /dev/serial/by-id when it is connected to the host. There is an LED on the board that is controllable and I can set it on or off using the GPIO pins to set at micro-controller startup options so the code seems to be running in some fashion.

The klipper.bin file works the same way if it is loaded using DFU or ST-Link.

I have gone through the Klipper source and nothing jumps out at me that appears to be an issue or some hardware in the STM32F070x6 is not properly accessed.

I fear I have two issues and I’m hoping that somebody here can help me out:

  1. What is the “Device’s firmware is corrupt” message I get during the DFU download of the STM32F070x6 firmware? I did go through the DFU-Util source and tried to capture the stderr from the download operation but there was no useful information. This doesn’t make sense because, as far as I can tell, there isn’t any special data/bytes inserted in the build of a currently supported MCU that doesn’t get this message.
  2. Any idea where I can look for the USB port to become recognized by the host? As far as I can tell, the USB port on the STM32F070x6 is standard for STM32 MCUs and there shouldn’t be any surprises there.

Thanx!

myke

Alas, it can be really tricky to bring up a new board. I can only guess at what the issue may be, so take what I say here “with a grain of salt”.

At first glance, I’m not sure the changes you’ve made are correct. Try going back to the mainline Klipper code and make just the changes here:

--- a/src/stm32/Kconfig
+++ b/src/stm32/Kconfig
@@ -15,7 +15,7 @@ config STM32_SELECT
     select HAVE_CHIPID
     select HAVE_STEPPER_BOTH_EDGE
     select HAVE_BOOTLOADER_REQUEST
-    select HAVE_LIMITED_CODE_SIZE if MACH_STM32F031 || MACH_STM32F042
+    select HAVE_LIMITED_CODE_SIZE if FLASH_SIZE < 0x10000
 
 config BOARD_DIRECTORY
     string
@@ -117,6 +117,10 @@ config MACH_STM32F103x6
     depends on LOW_LEVEL_OPTIONS && MACH_STM32F103
     bool "Only 10KiB of RAM (for rare stm32f103x6 variant)"
 
+config MACH_STM32F070x6
+    depends on LOW_LEVEL_OPTIONS && MACH_STM32F070
+    bool "Only 6KiB of RAM (for rare stm32f070x6 variant)"
+
 config MACH_STM32F0
     bool
 config MACH_STM32F1
@@ -211,8 +215,8 @@ config CLOCK_FREQ
 
 config FLASH_SIZE
     hex
-    default 0x8000 if MACH_STM32F031 || MACH_STM32F042
-    default 0x20000 if MACH_STM32F070 || MACH_STM32F072
+    default 0x8000 if MACH_STM32F031 || MACH_STM32F042 || MACH_STM32F070x6
+    default 0x20000 if (MACH_STM32F070 || MACH_STM32F072) && !MACH_STM32F070x6
     default 0x10000 if MACH_STM32F103 || MACH_STM32L412 # Flash size of stm32f103x8 (64KiB)
     default 0x40000 if MACH_STM32F2 || MACH_STM32F401 || MACH_STM32H723
     default 0x80000 if MACH_STM32F4x5 || MACH_STM32F446
@@ -234,7 +238,8 @@ config RAM_SIZE
     hex
     default 0x1000 if MACH_STM32F031
     default 0x1800 if MACH_STM32F042
-    default 0x4000 if MACH_STM32F070 || MACH_STM32F072
+    default 0x1800 if MACH_STM32F070x6
+    default 0x4000 if (MACH_STM32F070 || MACH_STM32F072) && !MACH_STM32F070x6
     default 0x2800 if MACH_STM32F103x6
     default 0x5000 if MACH_STM32F103 && !MACH_STM32F103x6 # Ram size of stm32f103x8
     default 0x8000 if MACH_STM32G431

You really want to use as much of the existing stm32f070 code as possible.

If you still have failures with that, try bringing up a UART connection instead of USB. The USB protocol is very sensitive to timing and initial board bringup can be difficult. If UART doesn’t come up, try seeing if there is any UART traffic (the mcu should send a stats report every 5 seconds).

FYI, if you make small changes to the code, it’s easier to share them by running git diff and then copying the resulting output into a block comment here (as I’ve done with my proposed changes above). These diffs can also be applied using the git apply command.

Hope that helps a little,
-Kevin

Is it tricky to bring up a new board or a new MCU? I’ve done three custom boards with the STM32G0B1 and all work fine.

Going further, I should also point out that DFU (using USB) on this board appears to work fine - I can ST Link into the MCU and see that the code was loaded into the STM32F070x6 correctly.

So, I believe we have a software issue here.

I would have thought that this goes without saying. My hope was that I wouldn’t have to change any Klipper source files (although… See below).


I set up my own Klipper branch: GitHub - mykepredko/klipper_stm32f070x6: Klipper is a 3d-printer firmware - mykepredko stm32f070x6 test changes then took your patch code, and put it into a patch file and made the changes using git apply based on that the build took place without errors, they seem to have been applied correctly.

I ran make menuconfig, build was okay but flagged that the normal build was larger than the Flash space (a good sign), I rebuilt with the following Optional Features (which I did with my build):

It should be noted that the build using your changes was flagged as “dirty” as was mine.

Then, I did a DFU-Util load into my board and received the same corrupt firmware error as with my code:

biqu@rpi5:~/klipper_stm32f070x6 $ sudo dfu-util -R -a 0 -s 0x08000000:force:mass-erase:leave -D ~/klipper_stm32f070x6/ou
t/klipper.bin -d 0483:df11
dfu-util 0.11

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2021 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

dfu-util: Warning: Invalid DFU suffix signature
dfu-util: A valid DFU suffix will be required in a future dfu-util release
Opening DFU capable USB device...
Device ID 0483:df11
Device DFU version 011a
Claiming USB DFU Interface...
Setting Alternate Interface #0 ...
Determining device status...
DFU state(10) = dfuERROR, status(10) = Device's firmware is corrupt. It cannot return to run-time (non-DFU) operations
Clearing status
Determining device status...
DFU state(2) = dfuIDLE, status(0) = No error condition is present
DFU mode device DFU version 011a
Device returned transfer size 2048
DfuSe interface name: "Internal Flash  "
Performing mass erase, this can take a moment
Downloading element to address = 0x08000000, size = 30992
Erase           [=========================] 100%        30992 bytes
Erase    done.
Download        [=========================] 100%        30992 bytes
Download done.
File downloaded successfully
Submitting leave request...
Transitioning to dfuMANIFEST state
dfu-util: can't detach
Resetting USB to switch back to Run-Time mode
biqu@rpi5:~/klipper_stm32f070x6 $ 

I repeated my test of turning off an LED in the Klipper build, as I did with my modifications, and I got the same result - by specifying the LED pin, I can have it on or off after booting.

My next thought was to run my code and do a kdiff on them but I remembered that I changed src/stm32/spi.c because the STM32F070x6 doesn’t have two SPI ports (which is the default for the Klipper code). When doing this build, I specified the same Optional features as above.

Regardless, I did do a kdiff on the two klipper.bin files and they are radically different. So, I’m not sure what to make of that.

Here they are if that’s helpful at all:
klipper-kevin & myke images.zip (44.4 KB)

Next steps:

First, I would like to understand what the “Device’s firmware is corrupt” message in DFU-Util means. In looking through the DFU-Util code, that seems to indicate that it is expecting the bytes “DFU” in the firmware file as well as a checksum. I did spend some time looking through the Klipper code (as well as the generated klipper.bin files) and didn’t see anything like that.

Could you explain why this message is coming up? I’ve put Klipper on quite a few boards and I’ve never seen that message - nobody seems to have reported it in any of the online forums and it should be noted that it comes up before the programming operation (and seems to go away after “Clearing status”).

Secondly, I guess I’ll run a couple of fly wires from the USB/Serial lines and see if Klipper comes up on the serial port. That will determine what happens next.

Comments or any other ideas?

Thanx for taking the time to look at this.

myke

I don’t know. I tend to completely ignore dfu-util output unless it reports an error return (that is, it completely fails without programming anything).

If you’re concerned about the flash, try flashing, then put the chip back into dfu mode, read the flash back out with dfu-util, and then use cmp myreadflash.bin out/klipper.bin to check if the two match.

Are you using the tssop20 package? If so, you don’t have access to PA11/PA12, so can’t do USB on that. Bringing the chip up in UART would be a good test. If you are in that situation, you could also try the following patch and select USB on PA9/PA10 (totally untested):

--- a/src/stm32/Kconfig
+++ b/src/stm32/Kconfig
@@ -15,7 +15,7 @@ config STM32_SELECT
     select HAVE_CHIPID
     select HAVE_STEPPER_BOTH_EDGE
     select HAVE_BOOTLOADER_REQUEST
-    select HAVE_LIMITED_CODE_SIZE if MACH_STM32F031 || MACH_STM32F042
+    select HAVE_LIMITED_CODE_SIZE if FLASH_SIZE < 0x10000
 
 config BOARD_DIRECTORY
     string
@@ -117,6 +117,10 @@ config MACH_STM32F103x6
     depends on LOW_LEVEL_OPTIONS && MACH_STM32F103
     bool "Only 10KiB of RAM (for rare stm32f103x6 variant)"
 
+config MACH_STM32F070x6
+    depends on LOW_LEVEL_OPTIONS && MACH_STM32F070
+    bool "Only 6KiB of RAM (for rare stm32f070x6 variant)"
+
 config MACH_STM32F0
     bool
 config MACH_STM32F1
@@ -211,8 +215,8 @@ config CLOCK_FREQ
 
 config FLASH_SIZE
     hex
-    default 0x8000 if MACH_STM32F031 || MACH_STM32F042
-    default 0x20000 if MACH_STM32F070 || MACH_STM32F072
+    default 0x8000 if MACH_STM32F031 || MACH_STM32F042 || MACH_STM32F070x6
+    default 0x20000 if (MACH_STM32F070 || MACH_STM32F072) && !MACH_STM32F070x6
     default 0x10000 if MACH_STM32F103 || MACH_STM32L412 # Flash size of stm32f103x8 (64KiB)
     default 0x40000 if MACH_STM32F2 || MACH_STM32F401 || MACH_STM32H723
     default 0x80000 if MACH_STM32F4x5 || MACH_STM32F446
@@ -234,7 +238,8 @@ config RAM_SIZE
     hex
     default 0x1000 if MACH_STM32F031
     default 0x1800 if MACH_STM32F042
-    default 0x4000 if MACH_STM32F070 || MACH_STM32F072
+    default 0x1800 if MACH_STM32F070x6
+    default 0x4000 if (MACH_STM32F070 || MACH_STM32F072) && !MACH_STM32F070x6
     default 0x2800 if MACH_STM32F103x6
     default 0x5000 if MACH_STM32F103 && !MACH_STM32F103x6 # Ram size of stm32f103x8
     default 0x8000 if MACH_STM32G431
@@ -384,7 +389,8 @@ choice
         bool "USB (on PA11/PA12)" if HAVE_STM32_USBFS || HAVE_STM32_USBOTG
         select USBSERIAL
     config STM32_USB_PA11_PA12_REMAP
-        bool "USB (on PA9/PA10)" if LOW_LEVEL_OPTIONS && MACH_STM32F042
+        bool "USB (on PA9/PA10)"
+        depends on LOW_LEVEL_OPTIONS && (MACH_STM32F042 || MACH_STM32F070x6)
         select USBSERIAL
     config STM32_USB_PB14_PB15
         bool "USB (on PB14/PB15)"
diff --git a/src/stm32/stm32f0.c b/src/stm32/stm32f0.c
index 72fc1645e..d7af831e3 100644
--- a/src/stm32/stm32f0.c
+++ b/src/stm32/stm32f0.c
@@ -186,10 +186,8 @@ armcm_main(void)
     hsi14_setup();
 
     // Support pin remapping USB/CAN pins on low pinout stm32f042
-#ifdef SYSCFG_CFGR1_PA11_PA12_RMP
     if (CONFIG_STM32_USB_PA11_PA12_REMAP || CONFIG_STM32_CANBUS_PA11_PA12_REMAP)
-        SYSCFG->CFGR1 |= SYSCFG_CFGR1_PA11_PA12_RMP;
-#endif
+        SYSCFG->CFGR1 |= 1<<4; // SYSCFG_CFGR1_PA11_PA12_RMP
 
     sched_main();
 }

Hope that helps,
-Kevin

Thanx for the comment about DFU-Util. I’ll ignore the firmware corrupt message for now.

Yeah, I think I figured that out on my own. I originally assumed that because DFU works, that the pins would be configured for USB by default.

Last night, I spent some time with the part Datasheets and they seem to indicate that Pins 17/18 come up as PA9/PA10 by default rather than PA11/PA12 as required for USB:


If I’m reading this correctly, then changing the pin mux to PA11/PA12 (and UXB) should be as simple as just setting the PA11_PA12_RMP bit in SYSCFG_CFGR1:


Looking over your patch - it looks like you’ve done the same analysis and are adding the same capability as is on the STM32F042.

I’m not sure if the changes will be a problem for the STM32F042 as you’re changing the predefined remap value to the hard coded bit value. Something to think about if things work.

I’ll give that try in a bit - I have a couple of things to work through first.


Just so you know where I was going with things:

Before I studied your patch (and got your email), I worked out the following plan to test the hypothesis and I’ll follow it if there’s no joy with your patch.

My plan is to:

  1. Look at the voltages on Pins 17/18 of the package while in DFU mode and then after loading the Klipper firmware.
    a) I’ve got to figure out the procedure here. I’m guessing that I will have to power the board separately and look at the pins in DFU Mode (Boot-Reset-Boot buttons) and with the Firmware installed (Reset button only).
  2. If I see a difference (I expect that the pins will be in a high impedance state after reset whereas when in DFU mode, they’ll be driving a USB present signal), then I’ll burn the Firmware with USART1 as the Klipper communications interface and look at the pins again (in this case I expect to see Pin 17 (“PA9”) high as it is NRZ Serial output).
  3. If this is the case, then I’ll solder in a fly wire connection to the rPi’s UART0 pins and see if Klipper can communicate. This will mean that Pins 17/18 come up as PA9/PA10.
  4. If everything I’m proposing here is correct and I see what I’m expecting then I’ll try to figure out where to write to bit 4 (PA11_PA12_RMP of SYSCFG1_CFGR1) in the Klipper Firmware and see if USB becomes available.

It does - I’ll let you know how things work for me with the patch and whether or not I went through my proceess.

Thanx!

Good news!!

The changes outlined in the latest patch produces a that works with USB as well as handling basic LEDs, the ADC pins (more below) and the SPI port accessing an LIS2DW. I seem to have a problem controlling NeoPixel LEDs which I think is a signal integrity issue with my design and I haven’t verified that the endstop sensor circuitry is working (have to play around with the LED definitions so that I can use SET_PIN .

But, overall, I’m very happy with what I’m seeing. The connection seems to be reliable.

Here’s a klippy.log from a while ago for your perusal:
klippy-stm32f070x6.log (1.1 MB)

A few questions for you:

  1. I’m confused as to how the reassignment works on devices other than the STM32F070x6. When I’ve worked with other MCUs, there isn’t the explicit need to do the remapping of the PA9/PA10 and PA11/PA12 pins. Now, a big difference is that these other devices have actual, individual PA9/PA10 pins as well as PA11/PA12 where as in the STM32F070x6, there are only two pins and it seems there is a mux between the physical pins and the pin circuitry on the chip. Can you comment at all?
  2. In the changes to /src/stm32/stm32f0.c, you explicitly set the PA11_PA12_RMP bit in the SYSCFG_CFGR1 register. This didn’t make sense to me so I put in the label only to find that it wasn’t recognized. It doesn’t seem like stm32f070x6.h is getting included in stm32f0.c before it is compiled. Shouldn’t it be brought in? Since it isn’t, should there be an error? I haven’t looked through the code to see if the include is conditional but I would have expected that without this include, the compile would fail.
  3. In my board, I specified PA3 as my temperature probe input but I got “ADC Value out of range”. I modded one of my boards with a fly wire so that PA2 would be the ADC input and things work fine. Why wouldn’t PA3 work here?
  4. When I ran my changes, I had a build error with the SPI port definition was looking for two SPI ports, but the STM32F070x6 has only one. Why isn’t there a build error with your code?
  5. What are next steps? I would think that the changes would have to be verified by other people with an important point being that it needs to be verified on the STM32F042 - I’m wondering if these changes will affect that device.

Thanx for your work putting this patch together.

It’s really appreciated.

Great!

Other devices have separate pins for PA11 and PA12 and don’t need any kind of remapping. My “reading between the lines” of the spec is that there weren’t enough pins to provide PA11 and PA12, but they realized that would be extremely limiting since it wouldn’t allow one to use USB. So, they added an internal hack that would reroute PA11/PA12 to the nominal PA9 and PA10 pins. I’m just guessing though. As you’re aware, one must set that bit to use USB on the device. Other devices will have all four of PA9, PA10, PA11, and PA12 pins and thus have no need to do remapping.

In Klipper, we try to compile for the largest commonly used class of chip. So, we compile for the stm32f070xb, stm32f103xe, etc. We try to avoid compiling for every minor chip revision, as that causes lots of extra maintenance work. If you look at the underlying C header files, the different stm32 chip revisions within the same line are nearly indentical - the larger chips typically just have more definitions. There’s no harm in a C header defining additional definitions.

The SYSCFG_CFGR1_PA11_PA12_RMP definition is one of the rare flags that was defined in stm32f070x6.h but not defined in the more expansive stm32f070xb.h file. It’s not worth the C preprocessor gymnastics to include that header file for just the one definition, so I open coded the 1<<4.

Alas, I don’t know.

See above. There’s no harm in having extra (unused) definitions available in stm32f070xb.h file.

The change is small enough I think we can commit it. As for the stm32f042, the only change was the open coding of 1<<4 which is the same on all stm32f0 chips so I don’t think it’s a notable risk.

Cheers,
-Kevin

Hey Kevin,

Thanx for the detailed reply.

I wondered about the needs for supporting the number of devices that you do and this gives me a better perspective on things.

A big difference between this part and more typically used STM32 devices is that all the IO (except for one) is on the PA port which doesn’t have the same capabilities as the other ports. I’m saying this because the voltage level on the pins specified for passing NeoPixel signals look strange. I’m noting it here in case somebody is looking at using the STM32F070x6.

Again, thank you for all your help. I have some work to do on this board but things are looking pretty good.

Keep well.