# Flashing the Blue Pill from macOS with an ST-Link V2

Quick reference for getting `build/3478A_VS_Display.bin` onto the STM32F103C8.
Verified working on 2026-07-20 (macOS / Apple Silicon, `st-flash` 1.8.0,
ST-Link V2 clone with firmware `V2J34S7`).

## Tools

`st-flash` / `st-info` come from the Homebrew `stlink` formula:

```bash
brew install stlink
```

No `sudo` and no admin password needed — unlike the ARM toolchain cask, this one
installs cleanly. OpenOCD is *not* required.

## Wiring

Four wires from the ST-Link V2 to the Blue Pill's SWD header (the 4-pin header at
the end of the board, opposite the USB connector):

| ST-Link V2 | Blue Pill SWD |
|------------|---------------|
| 3.3V       | 3V3           |
| SWDIO      | SWIO / DIO    |
| SWCLK      | SWCLK / DCLK  |
| GND        | GND           |

Both **BOOT0 and BOOT1 jumpers must be in the `0` position**. BOOT0 high boots the
chip's built-in system bootloader instead of the application at `0x8000000` — the
flash then succeeds but the program never runs.

## Step 1 — always probe first

```bash
st-info --probe
```

A healthy, connected target looks like this:

```
Found 1 stlink programmers
  version:    V2J34S7
  serial:     55FF6C066685485536271367
  flash:      131072 (pagesize: 1024)
  sram:       20480
  chipid:     0x410
  dev-type:   STM32F1xx_MD
```

`chipid: 0x410` is the STM32F103. (Blue Pill clones commonly report **128 KiB**
flash even though the C8 part is nominally 64 KiB — that is normal and harmless.)

Failure modes seen in practice:

- **`Found 0 stlink programmers`** — the ST-Link itself is not on USB, *or* the
  calling process has no raw-USB permission. Check with:
  `system_profiler SPUSBDataType | grep -iA3 stlink` (VID `0x0483`, PID `0x3748`).
  ⚠️ Claude Code's Bash sandbox blocks raw USB, so `st-info`/`st-flash` report zero
  programmers unless the sandbox is disabled. **Run flashing commands in a normal
  terminal.**
- **`Failed to enter SWD mode` with `chipid: 0x000`, `flash: 0`, `sram: 0`** — the
  ST-Link enumerated fine but got no answer from the MCU. Target has no 3.3 V, GND
  isn't shared, SWDIO/SWCLK are loose or swapped, or BOOT0 is high.

## Step 2 — flash

Via the Makefile target (wraps the `st-flash` line below):

```bash
make flash GCC_PATH="$HOME/toolchains/arm-gnu-toolchain-14.2.rel1-darwin-arm64-arm-none-eabi/bin"
```

Or directly — this is what the target expands to:

```bash
st-flash --reset write build/3478A_VS_Display.bin 0x8000000
```

The `.bin` carries no address information, hence the explicit `0x8000000` (start of
flash on the F103). The Intel-HEX file carries its own addresses, so with it you
drop the offset and declare the format instead:

```bash
st-flash --format ihex --reset write build/3478A_VS_Display.hex
```

A good run ends with:

```
19/19 pages written
Flash written and verified! jolly good!
NRST is not connected --> using software reset via AIRCR
```

`NRST is not connected` is expected with the 4-wire hookup above — `st-flash` falls
back to a software reset via AIRCR, so the board starts running immediately without
a power cycle.

## Fallbacks

Connect while holding reset, for a target whose firmware reconfigures the SWD pins
early in boot or that only answers intermittently:

```bash
st-flash --connect-under-reset --reset write build/3478A_VS_Display.bin 0x8000000
```

Many Blue Pill clones ship read-protected, which shows up as a write that fails
immediately. A full chip erase clears RDP; the normal write then succeeds:

```bash
st-flash erase
```

## Step 3 — confirm it is running

- **PC13 LED blinking** (~12 Hz — it toggles once per ~40 ms main-loop pass) means
  the main loop is turning over.
- **Power-on splash**: ~0.8 s of `##############` plus all 12 annunciators cycling
  through 10 colours, ending green. See the splash section in `CLAUDE.md`.
- Afterwards the screen is **blank and black** until the 3478A sends data — that is
  correct, not a fault. The backlight sits at **10 %** (`BACKLIGHT` in `lt7680.h`),
  so check in subdued light.
- A lit backlight on its own already proves MCU + SPI1 + LT7680 + panel are alive,
  since the brightness command travels over SPI.

If the flash verified but PC13 stays dark, suspect BOOT0 (see Wiring above).
