Env blackpill_f401ce consistently creates bad binaries that hangs the MCU

Edit: as pointed by @maxgerhardt, the issue relates to expectations about external crystal frequencies. The arduino f401cc and f411ce expect 25Mhz by default while the f401ce expects 8Mhz. An explicit specification in platformio.ini of the actual crystal frequency uses solves the problem. For example

build_flags =
    -D HSE_VALUE=25000000

TL;DR: env:blackpill_f401ce / arduino seems to generate bad binaries. Similar configuration such as env:blackpill_f401cc / arduino and env:blackpill_f401ce / arduino work well.

I am testing 3 similar bare bone plackpills board with stm32f401cc, stm32f401c and stm32f411ce MCUs respectively. These MCUs are similar but have small, medium and large memory sizes respectfully. I am using an original stlink-v2 from ST and the latest platformio version on vscode/windows-10, and the arduino framework.

Using the ST MxCube IDE everything works well. I can create a blinkies for each of the three MCUs, run, it debug/single-step it, etc.

On platformio, I can do the same only for the small and large MCUs but not for the stm32f401ce. With the stm32f401ce, the build seems to be OK but the binary it generates causes the MCU it to hang and enter a state that require a SWD + hardware reset to access it again. (I use the ST programmer with SWD + hardware reset to erase the entire chip so I can use it again).

I tried different combinations and the consistent symptom isthat binaries that are created on platformio with the blackpill_f401ce env are bad somehow and hang the MCU. Everything else works well, including for example cross combinations such as building with env:blackpill_f401cc (small memory) env and running on the stm32lf401ce MCUs (medium memory).

This is the platformio configuration I use for the smt32f401ce (medium).

platform = ststm32
board = blackpill_f401ce
framework = arduino
debug_tool = stlink
upload_protocol = stlink

Any help will be greatly appreciated as I have no clue how to continue.

Since you’ve already connected an STLink to the board, debugging in VSCode should work out of the box and tell you where the MCU is stuck. What happens when you “Run -> Start Debugging” in VSCode and step through the code?

Also, what’s the crystal oscillator setup on your barebones board? None or an 8MHz crystal?

Thanks @maxgerhardt.

  1. All my boards have 25Mhz crystal and when I build a blinky binary with env:env:blackpill_f401cc/arduino, all the three CPU type runs that binary well and blink at the same rate.

  2. As for debugging, I tried it but the env:blackpill_f401ce binary is so bad that I cannot even break at Reset_Handler or main.

For example, here is an attempt to debug a simple blinky. It works when I use the f401cc env but when I try it with the f401ce env the MCU doesn’t break


platform = ststm32
board = blackpill_f401ce
framework = arduino
debug_tool = stlink
upload_protocol = stlink
build_flags = -Wl,-Map,output.map
debug_init_break = tbreak Reset_Handler

Debug log:

Processing blackpill_f401ce (platform: ststm32; board: blackpill_f401ce; framework: arduino)
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/ststm32/blackpill_f401ce.html
PLATFORM: ST STM32 (11.0.0) > BlackPill F401CE
HARDWARE: STM32F401CEU6 84MHz, 96KB RAM, 512KB Flash
DEBUG: Current (stlink) External (blackmagic, cmsis-dap, jlink, stlink)
 - framework-arduinoststm32 4.10900.200819 (1.9.0)
 - framework-cmsis 2.50501.200527 (5.5.1)
 - toolchain-gccarmnoneeabi 1.90201.191206 (9.2.1)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 10 compatible libraries
Scanning dependencies...
No dependencies
Building in debug mode
Linking .pio\build\blackpill_f401ce\firmware.elf
Checking size .pio\build\blackpill_f401ce\firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [          ]   0.9% (used 888 bytes from 98304 bytes)
Flash: [          ]   2.4% (used 12392 bytes from 524288 bytes)
========================= [SUCCESS] Took 8.51 seconds =========================
Reading symbols from c:\Users\User\Downloads\test4\.pio\build\blackpill_f401ce\firmware.elf...
undefinedC:\Users\User\.platformio\packages\toolchain-gccarmnoneeabi\bin\arm-none-eabi-gdb.exe: warning: Couldn't determine a path for the index cache directory.
PlatformIO Unified Debugger -> http://bit.ly/pio-debug
PlatformIO: debug_tool = stlink
PlatformIO: Initializing remote target...
xPack OpenOCD, x86_64 Open On-Chip Debugger 0.10.0+dev-00378-ge5be992df (2020-06-26-09:29)
Licensed under GNU GPL v2
For bug reports, read
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
Info : tcl server disabled
Info : telnet server disabled
Info : clock speed 2000 kHz
Info : STLINK V2J37S7 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.243243
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f4x.cpu on pipe
Info : accepting 'gdb' connection from pipe
target halted due to debug-request, current mode: Handler HardFault
xPSR: 0x21000003 pc: 0xfffffffe msp: 0x20000be8
Info : device id = 0x10016433
Info : flash size = 512 kbytes
Info : flash size = 512 bytes
0xfffffffe in ?? ()
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0xfffffffe msp: 0xfffffffc
Info : Unable to match requested speed 8000 kHz, using 4000 kHz
Warn : keep_alive() was not invoked in the 1000 ms timelimit. GDB alive packet not sent! (1028 ms). Workaround: increase "set remotetimeout" in GDB
Loading section .isr_vector, size 0x194 lma 0x8000000
Loading section .text, size 0x2b44 lma 0x8000194
Loading section .rodata, size 0x498 lma 0x8002cd8
Loading section .ARM, size 0x8 lma 0x8003170
Loading section .init_array, size 0x14 lma 0x8003178
Loading section .fini_array, size 0x8 lma 0x800318c
Loading section .data, size 0x8c lma 0x8003194
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08000b2c msp: 0x20018000
Start address 0x8000b2c, load size 12832
Transfer rate: 7 KB/sec, 1833 bytes/write.
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Unable to match requested speed 2000 kHz, using 1800 kHz
Error: timed out while waiting for target halted
TARGET: stm32f4x.cpu - Not halted
timed out while waiting for target halted
TARGET: stm32f4x.cpu - Not halted
Temporary breakpoint 1 at 0x8000b2c: file C:\Users\User\.platformio\packages\framework-arduinoststm32\system\Drivers\CMSIS\Device\ST\STM32F4xx\Source\Templates\gcc\startup_stm32f401xe.s, line 78.
PlatformIO: Initialization completed
PlatformIO: Resume the execution to `debug_init_break = tbreak Reset_Handler`
PlatformIO: More configuration options -> http://bit.ly/pio-debug
Note: automatically using hardware breakpoints for read-only addresses.
Warn : target not halted
Info : target stm32f4x.cpu was not halted when resume was requested
target not halted
target stm32f4x.cpu was not halted when resume was requested

I would post here the firmware.elf it generates but the system doesn’t let me.

Any suggestion will be greatly appreciated. Or experience of other uses of blackpill_f401ce/arduino.

Edit: Google search to “env:blackpill_f401ce” (with the double quotes) doesn’t yield results beyond the platformio documentation and makes me think that that configuration was actually used.


Okay then let’s take a look at how the Arduino core generates the clock. The variant is defined in

And we can see the clock setup

and for the HSE setup

So the comment says an 8MHz crystal is expected but the macro already evaluates to 25MHz.

Can you still test whether it makes a difference if you write

extern "C" void SystemClock_Config(void) {
 if (SetSysClock_PLL_HSI() == 0) {

in your scr\main.cpp file after the `#include <Arduino.h>?

Do I undestand you correctly that all binaries run correctly on the MCU, with the expected blink rate and such, but only the F401CE cannot be debugged properly?

Is the NRST of the STLink connected to the chip, too? Seems there’s a problem here with either the physical line or the OpenOCD setup.

@maxgerhardt, I think you found the problem. Greatly appreciated!

Adding this code to my main.cpp solves the problem and the binary that env:blackpill_f401ce creates now run properly in release and debug mode.

// TODO: how should we #include this properly?
extern "C"  int SetSysClock_PLL_HSI();

extern "C" void SystemClock_Config(void) {
 if (SetSysClock_PLL_HSI() == 0) {

If I got it correctly, Arduino assumes different default crystal speeds for f401cc (25Mhz), f401ce (8Mhz) and f411ce (25Mhz), while my boards are all 25Mhz. What is a clean way to tell Arduino that my project uses 25Mhz? Any flag or a -Dxxx macro I can add in platform.ini?

(as for NRST, it was connected so I can erase the hang chip with ST programmer, but platformio doesn’t seem to activate it, nor does it interfere with platformio upload/debug. I took it off now).

Hm that’s kinda magic then.

The PlatformIO board to Arduino variant mapping looks like

So both F401C(C|E) map to the same variant, just that the E version has a larger RAM and FLASH (96k and 512k RAM/FLASH in E version). And since they both map to the same arduino variant, the same clock init code is used as linked above, which by code expects a 25MHz crystal, contratry to what the comments wants one to believe.

I’ve also verified this with STM32CubeMX that the multiplies and dividors are correct assuming a 25MHz HSE

which again matches the code linked above.

The F411CE has different multipliers (since it also needs to get up to 100MHz)

But if that works there’s no problem there.

So it’s really magic to me because the HSE setup looks correct and the HSE_VALUE should be 25000000 (Hz). If you got the chip working now you can also Serial.println(HSE_VALUE) that.

I added the line below to my setup() function and examined the value with a breakpoint at the next statement. It’s 25000000 with env:blackpill_f401cc and env:blackpill_f411ce and is 8000000 with env:blackpill_f401ce.

uint32_t hse_value = HSE_VALUE;

I then added the flag below to platformio.ini, removed the SystemClock_Config() function I added earlier and everything works just fine now. :wink:

build_flags =
-D HSE_VALUE=25000000

@maxgerhardt, thanks a lot for your patient and help. I wouldn’t figure this myself in 100 years. Thinking about it, external crystal is indeed an hardware variant that should match the configuration. I am surprised though why the stlink/swd debugger could not break at Reset_Handler since it happens before any assumption about the external crystal is used.

… I found another anomaly with the f401ce, other than just having a different default crystal frequency. If I add an arbitrary crystal specification for the f401ce everything compiles well.

platform = ststm32
board = blackpill_f401ce
framework = arduino
debug_tool = stlink
upload_protocol = stlink
build_flags =
    -D HSE_VALUE=10000000

However, if I do the same for the f401cc or f411ce, I am getting many compile warning about redefining HSE_VALUE. For example:

platform = ststm32
board = blackpill_f411ce
framework = arduino
debug_tool = stlink
upload_protocol = stlink
build_flags =
    -D HSE_VALUE=10000000
C:\Users\User\.platformio\packages\framework-arduinoststm32\variants\Generic_F411Cx/variant.h:116: warning: "HSE_VALUE" redefined
  116 | #define HSE_VALUE    

I don’t know who owns this stuff but would be nice to have consistency among these variants.

Ahaa now it makes more sense. I did not expect the variant.h to redefine the HSE value, but only looked in the default CMSIS STM definitions.


So… with the F401Cx variants, only the CC is compiled for HSE=25MHz. And the other one, CE, somehow gets a wrong default value, 8MHz. For F411Cx variants, it also gets 25MHz with the CE version.

And when I take my time to google that for 5 seconds I also see that people have already recongized that

Not sure why it only sets 25MHz for the ARDUINO_BLACKPILL_F401CC variant not the CE variant too.

Do the commercial boards really have 8MHz crystal on the CE version? If all boards come with 25MHz, it’s an actual clock config bug in the Arduino STM32 core

Well in any case it explains why you can get an error when redefining the HSE_VALUE for F401CC and F411CE – it’s already defined for these variants and doesn’t do a #ifndef HSE_VALUE .. then change it...

I searched AliExpress for ‘blackpills’ with 8Mhz and could find only for the F401CE so this may explain why it has different configuration. WeAct STM32F401CEU6 STM32F401STM32F4 V 3,0 Entwicklung Bord Micropython PYBoard BlackPill Arduino|Demo-Board| - AliExpress

However, the 8MHZ and 25MHZ configurations in the links you posted seems to be applied to all F4x1xx’s boards, including custom boards, not specifically to just blackpills, which doesn’t seem right. (I assume that Arduino support some range of HSE_VALUE and can compute the proper clock parameters in that range).

Does this make sense? Is it reasonable to send a pull request for conditional setting as you suggested?

#ifndef HSE_VALUE
#define HSE_VALUE               25000000U
#ifndef HSE_VALUE
#define HSE_VALUE               25000000U

Edit: my argument above about custom boards doesn’t hold since the settings here are specific for blackpills. I guess it’s reasonable the way it is.

I think yes even though commercial the commercial blackpills are set up with the crystal config as Arduino expects them, it doesn’t hurt to always let the user choose the HSE_VALUE by checking if it hasn’t been previously defined. Just defensive programming and usefull for custom boards that are so close to a blackpill but just differ in that one aspect.

Proposed in Prevent redefine of HSE_VALUE if already user-overridden by maxgerhardt · Pull Request #1281 · stm32duino/Arduino_Core_STM32 · GitHub.

Thanks @maxgerhardt. Your help is greatly appreciated here.

I was just able to upgrade my real project from stm32f401cc to stm32f401ce by just soldering a new MCU, adding that HSE_VALUE macro and changing platformio.ini to the 401ce env and board and it works great, with the increased RAM I needed. This is my first project with stm32 and I am impressed how compatible the MCUs are.

1 Like

PR got approved and merged today that allows arbitrary reconfig of HSE_VALUE for these boards too, to accomodate differening crystal configurations :slight_smile:

That was quick. :wink:

Thanks @maxgerhardt.