Migrating STM32F103 to GD32F103: a checked pitfall list
GD32F103 is a P2P-graded, engineering-review candidate for STM32F103 — the pinout and register map match, and GigaDevice publishes an official compatibility document. It is not identical silicon: the eleven differences below are the ones that actually break firmware and hardware in migration, each traceable to the official documentation. Work the list top to bottom before first power-on; items 1–3 catch most failed bring-ups.
Sources: GigaDevice "Compatibility sum-up between GD32 and STM32" V2.0 and the GD32F103 software migration guide — both archived in our evidence store. Where a pitfall applies only to specific densities, that is stated.
1. BOOT0 must be pulled down — floating works on ST, not on GD
Symptom: The board boots fine with an STM32F103 but a GD32F103 starts unreliably or enters the bootloader. On ST parts BOOT0 may float when booting from flash; GigaDevice requires it pulled down.
What to do: Fit a 10 kΩ pull-down on BOOT0. This is the single most common hardware change and costs one resistor.
2. Minimum supply is 2.6 V, not 2.0 V
Symptom: Designs that ride through brown-outs at 2.0–2.6 V on ST silicon fail on GD32F10x, whose specified range is 2.6–3.6 V. High-power modules (Wi-Fi, GSM, GPS) sharing the 3.3 V rail can dip the LDO output into exactly this window at power-on.
What to do: Review the power budget and LDO output capacitance (GigaDevice suggests low-ESR, e.g. tantalum, and a larger value). Residual VCC above the power-on-reset threshold but below 2.6 V can also block start-up.
3. HSE start-up takes longer — raise HSE_STARTUP_TIMEOUT
Symptom: Clock init intermittently falls back to HSI because the external oscillator is not released before the ST library's default timeout expires. GD32F10x start-up time can reach hundreds of milliseconds.
What to do: Change HSE_STARTUP_TIMEOUT from 0x0500 to 0xFFFF in the device header (per GigaDevice's software migration guide). Also re-check wake-up timing in low-power flows.
4. 108 MHz ceiling changes every hard-coded timing assumption
Symptom: Delay loops, timer reloads and baud-rate maths calibrated for 72 MHz run fast if the PLL is reconfigured for 108 MHz. A documented field case: raising SYSCLK broke a 1200-baud UART until the APB divider was changed.
What to do: Either keep the PLL at 72 MHz for the first article, or audit every clock-derived constant (SysTick, timer prescalers, UART/I2C/SPI dividers) before using the headroom. USB still needs a valid 48 MHz derivation.
5. Flash timing differs in both directions
Symptom: Execution: the code area is zero-wait (SRAM-buffered), so cycle-counted routines run faster than on ST. Erase/program: a 1 KB page erase is ~60 ms versus ~30 ms on ST, so IAP and option-byte code with ST's default timeouts fails.
What to do: Raise EraseTimeout/ProgramTimeout (GigaDevice suggests 0x000FFFFF / 0x0000FFFF). On parts above 256 KB, the data area executes with large delay — keep real-time code in the code area (scatter-load if needed).
6. Unlock/option-byte sequences need a readback
Symptom: On GD32F10x, flash option-byte operations copied verbatim from ST's library intermittently fail because the unlock state is not yet effective when the next write lands.
What to do: After writing the KEY register sequence, poll the control register (or insert two NOPs) before proceeding, per the official compatibility document.
7. ADC behaves differently in five specific ways
Symptom: Readings stuck at zero, values lower than expected, or ADC2 sampling zero in sync mode — firmware that worked on ST.
What to do: Configure sample pins as analog input (ST tolerates floating; GD does not). Keep ADC clock at or below the allowed maximum (14 MHz on F1). On sub-256 KB F103/F101, insert ~20 µs delay after ADC enable. Input impedance is lower — lengthen sample cycles or reduce divider resistance. Validate absolute accuracy in-circuit; GigaDevice's own document notes it is slightly lower.
8. USART inserts an idle bit between bytes
Symptom: Back-to-back byte streams show inter-byte gaps that trip tight receiver-side timeout logic or bit-banged protocol emulation on the other end.
What to do: Budget for the idle bit in protocol timing, or verify the receiving side tolerates it. Continuous-transmission assumptions from ST do not carry over.
9. DMA flag and request timing differ
Symptom: Occasional data dislocation on ADC+DMA, or a stale DMA request being answered immediately after re-enable.
What to do: Clear DMA requests explicitly when reconfiguring (reset the peripheral's DMA enable bit in the transfer-complete handler), and re-test every DMA-driven peripheral rather than assuming ST behaviour.
10. SWD drive is weaker — programming can fail on long leads
Symptom: "Flash download failed" from Keil/IAR with cables that work fine on ST boards.
What to do: Add 10 kΩ pull-up on SWDIO and 10 kΩ pull-down on SWCLK, keep the debug lead short (under ~20 cm) and drop the adapter clock to 4 MHz or below.
11. Sleep currents are an order of magnitude higher
Symptom: Battery products: GD32F103 (≤256 KB class) deep-sleep is ~1.4 mA versus ~24 µA on ST, and standby ~10.5 µA versus ~3.4 µA. Run current is lower (~32 mA vs ~52 mA at 72 MHz), which can mask the problem in bench tests.
What to do: Re-measure the real duty-cycle power budget. For designs that live in deep-sleep, this difference alone can disqualify the swap — check it before committing the BOM.
Per-part starting points
The graded cross-reference pages list these differences per exact part with cited evidence: STM32F103C8T6, STM32F103CBT6, STM32F103RBT6, STM32F103RCT6, STM32F103VET6 — or browse the full STM32F103 family table.
A successful bench port is not a production qualification: validate ADC accuracy, USB enumeration, sleep currents and your production programming flow on the exact suffix and silicon lot you will ship.