src/platforms/arm/stm32/pins/README.md
This directory contains STM32 GPIO pin definitions organized by family and board variant.
pins/
├── README.md # This file - architecture guide
│
├── core/ # Unified framework (zero duplication)
│ ├── armpin_template.h # Single template for all STM32 families
│ ├── gpio_port_init.h # GPIO port initialization macros
│ └── pin_macros.h # Pin definition macros
│
├── families/ # Family-specific variants
│ ├── stm32f1.h # F1 family (HAS_BRR=true)
│ ├── stm32f2.h # F2 family (HAS_BRR=false)
│ └── stm32f4.h # F4 family (HAS_BRR=false)
│
├── boards/ # Board-specific pin mappings
│ ├── f1/ # STM32F1 boards
│ │ └── (future boards here)
│ ├── f2/ # STM32F2 boards
│ │ └── (future boards here)
│ └── f4/ # STM32F4 boards
│ ├── f401cx_blackpill.h # BlackPill F401CC/F401CE
│ ├── f401re_nucleo.h # Nucleo F401RE
│ ├── f407vg_disco.h # Discovery F407VG
│ ├── f411ce_blackpill.h # BlackPill F411CE
│ ├── f411re_nucleo.h # Nucleo F411RE
│ ├── f446re_nucleo.h # Nucleo F446RE
│ └── f4x9zi_nucleo.h # Nucleo F429ZI/F439ZI
│
├── fastpin_dispatcher.h # Main dispatcher (board detection)
└── fastpin_legacy.h # Legacy fallback (compatibility)
The Problem: Different STM32 families use different GPIO registers to clear pins:
BRR (Bit Reset Register) at offset 0x28BRR register - must use upper 16 bits of BSRR insteadThe Solution: Single template with compile-time HAS_BRR parameter:
template<uint8_t PIN, uint8_t _BIT, uint32_t _MASK, typename _GPIO, bool HAS_BRR>
class _ARMPIN_STM32 {
inline static void lo() {
if constexpr (HAS_BRR) {
_GPIO::r()->BRR = _MASK; // F1, L4, G0, G4
} else {
_GPIO::r()->BSRR = (_MASK << 16); // F2, F4, F7, H7
}
}
};
Benefits:
if constexpr compiled away to single instructionLayer 1: Core Framework (core/)
armpin_template.h - Unified template for all familiesgpio_port_init.h - Port initialization macrospin_macros.h - Pin definition macrosLayer 2: Family Variants (families/)
HAS_BRR parameter appropriatelystm32f4.h sets HAS_BRR=false, initializes ports A-KLayer 3: Board Mappings (boards/)
f411ce_blackpill.h defines pin 0 = PA0, pin 1 = PA1, etc.Old Approach (Implicit):
#if defined(SPARK)
#include "fastpin_stm32_spark.h"
#elif defined(__STM32F1__)
#include "fastpin_stm32f1.h"
#elif defined(STM32F4)
#include "fastpin_stm32f4.h"
#endif
New Approach (Explicit):
// F4 Family - BlackPill F411CE
#if defined(ARDUINO_BLACKPILL_F411CE)
#include "families/stm32f4.h"
#include "boards/f4/f411ce_blackpill.h"
// F4 Family - Nucleo F411RE
#elif defined(ARDUINO_NUCLEO_F411RE)
#include "families/stm32f4.h"
#include "boards/f4/f411re_nucleo.h"
// ... more boards ...
#else
#error "STM32: Unknown board. See src/platforms/arm/stm32/pins/README.md"
#endif
Benefits:
Step 1: Create family variant (if not exists)
// families/stm32f7.h
#pragma once
#include "../core/armpin_template.h"
#include "../core/gpio_port_init.h"
#include "../core/pin_macros.h"
namespace fl {
// F7 has no BRR register (like F4)
#define _DEFPIN_ARM_F7(PIN, BIT, PORT) _DEFPIN_STM32(PIN, BIT, PORT, false)
// Initialize available ports
_STM32_INIT_PORT(A); _STM32_INIT_PORT(B); _STM32_INIT_PORT(C);
_STM32_INIT_PORT(D); _STM32_INIT_PORT(E);
#if defined(GPIOF)
_STM32_INIT_PORT(F);
#endif
// ... more ports ...
}
Step 2: Create board mapping file
// boards/f7/nucleo_f767zi.h
#pragma once
// Map Arduino pin numbers to GPIO pins
_DEFPIN_ARM_F7(0, 9, G); // Pin 0 = PG9
_DEFPIN_ARM_F7(1, 14, G); // Pin 1 = PG14
// ... rest of pins ...
Step 3: Register in dispatcher
// fastpin_dispatcher.h
#elif defined(ARDUINO_NUCLEO_F767ZI)
#include "families/stm32f7.h"
#include "boards/f7/nucleo_f767zi.h"
Total: ~30 lines for complete new family support (vs ~200 lines with old approach)
FastPin templates are indexed by Arduino digital pin numbers (0, 1, 2, ...), not pin names (PA0, PB0).
Why? STM32duino defines PA0, PB0 as macros that expand to board-specific pin numbers:
// On BlackPill F411CE:
#define PA_0 0 // Arduino pin 0 = PA0
#define PA_1 1 // Arduino pin 1 = PA1
// On Nucleo F411RE (different mapping):
#define PA_0 10 // Arduino pin 10 = PA0
#define PA_1 11 // Arduino pin 11 = PA1
Solution: Board mapping files define the Arduino pin → GPIO mapping:
// In boards/f4/f411ce_blackpill.h:
_DEFPIN_ARM_F4(0, 0, A); // Arduino pin 0 = PA0
_DEFPIN_ARM_F4(1, 1, A); // Arduino pin 1 = PA1
| Family | HAS_BRR | Status | Example Boards |
|---|---|---|---|
| STM32F1 | Yes (0x28) | ✅ Migrated | BluePill F103C8, Maple Mini, Generic F103C8 |
| STM32F2 | No (use BSRR) | ✅ Migrated | Spark Core, Particle Photon |
| STM32F4 | No (use BSRR) | ✅ Migrated | BlackPill F411CE, Nucleo F401RE, Disco F407VG |
| STM32F0 | Yes (0x28) | 📋 Future | Nucleo F030R8, Nucleo F091RC |
| STM32F3 | Yes (0x28) | 📋 Future | Nucleo F303RE, Discovery F3 |
| STM32F7 | No (use BSRR) | 📋 Future | Nucleo F767ZI, Discovery F746 |
| STM32H7 | No (use BSRR) | 📋 Future | Nucleo H743ZI, Portenta H7 |
| STM32L4 | Yes (0x28) | 📋 Future | Nucleo L476RG, Discovery L4 |
| STM32G0 | Yes (0x28) | 📋 Future | Nucleo G071RB |
| STM32G4 | Yes (0x28) | 📋 Future | Nucleo G474RE |
variants/<board>/variant_*.cpp for pin mappings✅ Reorganization Complete - All phases finished. The new architecture is fully functional:
if constexpr compiled away)Legacy files preserved for backward compatibility with deprecation warnings:
fastpin_stm32f1.hfastpin_stm32f4.hfastpin_stm32_spark.hpin_def_stm32.hRecommended action: Remove legacy files in next major release (FastLED 4.0)
Error: "STM32: Unknown board"
fastpin_dispatcher.h#warning ARDUINO_* in your sketchError: Compilation fails with GPIO register errors
HAS_BRR settingPerformance concerns
if constexpr is compiled away - zero runtime overheadarm-none-eabi-objdump -d firmware.elf | grep -A5 "FastPin"