The overview, with examples running, is on the board page and The STM32F411 model (every register).

The STM32F411 Black Pill

A Cortex-M4 at 84 MHz with GPIO, four timers, USART1 and SysTick.

What it is

The WeAct MiniF4 "Black Pill", an STM32F411CEU6 running on our own Cortex-M core in its ARMv7-M profile with Thumb-2. The registers come from the manufacturer's reference manual and datasheet at their real addresses, so the same source cross-compiled against the vendor headers would work on silicon.

The clock is fixed: 84 MHz for the CPU, the bus and the TIM2 to TIM5 timers, and 42 MHz on the slower peripheral bus.

The pins

40 pins on a 0.1 inch pitch, USB-C at the top.

Left header, top to bottom: VBAT, PC13, PC14, PC15, NRST, PA0 to PA7, PB0, PB1, PB2, PB10, PA13, PA14, GNDb.

Right header: 3V3, GND, 5V, PB9, PB8, PB7, PB6, PB5, PB4, PB3, PA15, PA12, PA11, PA10, PA9, PA8, PB15, PB14, PB13, PB12.

PB11 is absent, because the 48-pin part does not bring it out. So port B is fifteen pins, not sixteen.

A pin here is a port and a number, not a board number: PA5, not 5. That catches people coming from an Arduino, and the learning path leads with it.

The LED is the other way around

PC13 carries the on-board blue LED with its anode to 3.3 V through 470 ohms and its cathode on the pin. It lights when PC13 is driven low. The board's first lesson is called "The LED that is on when the pin is low" for that reason.

What is modeled

GPIO, TIM2 to TIM5 in their counting and PWM modes, USART1 and SysTick, plus enough of RCC to answer the register reads a program makes on the way up.

Every peripheral on a real STM32 starts with its clock switched off, and that is true here too: enabling the clock before touching a peripheral is part of the exercise, not a formality this model skips.

What is not

The clock tree and flash latency, DMA, USB, the ADC, I2C, SPI, SDIO, the RTC, the watchdogs, TIM1 and TIM9 to TIM11, the CRC unit, low-power modes, the option bytes and flash programming. Their registers are unmapped, so touching one is a bus fault.

The ADC's absence is the one to watch: every PA pin and PB0/PB1 is an analog input on a real F411, and analogRead has nothing behind it here.

The clock tree not being modeled cuts the other way too. The core runs at 84 MHz whatever RCC is programmed to, so a sketch that forgets to start the PLL keeps time here and runs five times slow on a real board. The 3V3 and 5V pins are ideal sources that cannot sag.

Input capture is not modeled. WFI and WFE park the core; deep sleep behaves as ordinary sleep.

Vendor stacks (the Cube HAL, libopencm3, the STM32duino core) are out of scope for the same reason the Pico SDK is: they bring up hardware that is not here.

The register tables are in the STM32F411 model.

Serial

USART1 carries the serial monitor. The full detail, including what happens when the receive queue overflows, is in the contract document.

Stock Arduino libraries

A tutorial that includes Servo.h, Wire.h, SPI.h, EEPROM.h, LiquidCrystal_I2C.h, Adafruit_GFX.h with Adafruit_SSD1306.h and DHT.h compiles here as it stands. Each is Mokxi's own header under the upstream name, written on the drivers for the parts in the bin, and none of it is the upstream library's code.

  • Wire.begin() puts the I2C bus on PB7 (SDA) and PB6 (SCL).
  • EEPROM.h works, but this board's model has no EEPROM or writable flash, so the bytes live in 4 KB of RAM: they read back within a Run and are gone at a reset.
  • Adafruit_NeoPixel.h stops with a message: a WS2812 bit is too short to make by hand on this board, and only the ESP32-C3 and ESP32-C6 drive the strip.

The code editor and compiling lists what each one covers and how it differs from the upstream library.

The examples

Blink, Button, Serial, Fade and Chaser.

Elsewhere