2025-10-09 9:55 PM
Context
Custom 3D-printer controller that I'm developing from scratch, currently on rev7. Rev6 (very similar) runs the same blinky fine. On rev7 I can connect/erase/flash/verify over SWD, but code doesn’t run—GPIOs stay at ~0 V.
MCU / Tooling
MCU: STM32F446ZET6 (no bootloader)
Programmer: ST-Link V2, STM32CubeProgrammer (SWD)
Build: STM32Duino / Generic F446ZETx (144-pin), USB support = None, Clock = HAL, Debug = Serial Wire
Test firmware: blinker for the LEDs on PA8 and PA15
Symptoms
Flash + verify at 0x08000000 succeed.
Vector table @0x08000000 looks sane (SP=0x200xxxxx, PC=0x0800xxxx|1).
After reset, PA8 and PA15 don’t toggle (measured at MCU pins). PA15 blips only when debugger attaches.
Same binary works on rev6.
Measured the following:
VDD = 3.3 V on all banks (decoupled).
VDDA = 3.3 V, local 100 n + 1 µF.
VCAP1/VCAP2 ≈ 1.3 V each (2.2 µF caps fitted).
NRST ≈ 3.3 V idle, pulses on connect.
BOOT0 pulled low (confirmed low during reset).
What I tried
Full erase, re-flash minimal register blinker
Rebuilt with USB=None, HSI only.
Tried lower BOR level.
JTAG vs SWD: using Serial Wire to free PA15 (still no PA8 toggle).
Attachments
Schematic fragment: MCU core
Board photo around MCU
Would anyone know what could be wrong here? I've not encountered such an issue before where I could connect, interact, and flash the chip, but not be able to execute the sketch I flashed.
Any help would be greatly appreciated.
Thanks!
#include <Arduino.h>
#include <Servo.h>
// Pins
const uint32_t LED1 = PA8, LED2 = PA15; // PA15 PA12
const uint32_t BED = PD7, E0 = PD8, E1 = PD9;
const uint32_t BLT_SERVO_PIN = PD6; // orange
const uint32_t BLT_PROBE_PIN = PC12; // white (open-drain)
HardwareSerial Serial2(PA9, PA10); // (rx, tx)
#define LED_ACTIVE_HIGH 1
#define HEATER_ACTIVE_HIGH 1
const bool ENABLE_HEATER_DEMO = false;
const unsigned long LED_PERIOD_MS = 500;
const unsigned long HEATER_PERIOD_MS = 5000;
const unsigned long PROBE_CONFIRM_MS = 2; // LOW must be stable this long
const unsigned long SERVO_MASK_MS = 300; // ignore triggers after servo move
unsigned long t_led=0, t_heater=0;
bool led_state1=false, led_state2=true;
bool bed_state=false, e0_state=false, e1_state=false;
Servo blt;
volatile bool edgeSeen=false;
volatile unsigned long edgeAt=0;
unsigned long mask_until=0;
bool last_reported_low=false;
inline void setPin(uint32_t pin, bool on, bool ah){ digitalWrite(pin,(on ^ !ah)?HIGH:LOW); }
void mask_after_servo(){ mask_until = millis() + SERVO_MASK_MS; }
void blt_deploy(){ blt.write(10); mask_after_servo(); }
void blt_stow(){ blt.write(90); mask_after_servo(); }
void blt_self(){ blt.write(160); mask_after_servo(); }
void blt_reset(){ blt.write(120); mask_after_servo(); }
void blt_cycle(uint16_t dep=800,uint16_t stw=800){ blt_deploy(); delay(dep); blt_stow(); delay(stw); }
// ISR (plain STM32)
void probeISR(){ edgeSeen=true; edgeAt=millis(); }
void setup() {
pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT);
pinMode(BED, OUTPUT); pinMode(E0, OUTPUT); pinMode(E1, OUTPUT);
setPin(LED1,false,LED_ACTIVE_HIGH); setPin(LED2,false,LED_ACTIVE_HIGH);
setPin(BED,false,HEATER_ACTIVE_HIGH); setPin(E0,false,HEATER_ACTIVE_HIGH); setPin(E1,false,HEATER_ACTIVE_HIGH);
// Prefer external pull-up 4.7k–10k to 3.3V + 0.01–0.1uF to GND on PC12
pinMode(BLT_PROBE_PIN, INPUT); // if you lack external PU, use INPUT_PULLUP and FALLING
attachInterrupt(digitalPinToInterrupt(BLT_PROBE_PIN), probeISR, CHANGE);
blt.attach(BLT_SERVO_PIN);
blt_stow();
Serial2.begin(115200);
delay(50);
Serial2.println("\n[BLTouch Test + Debounce @ USART2]");
Serial2.println("d=deploy s=stow t=self r=reset c=cycle p=probe h=help");
t_led = t_heater = millis();
}
void loop() {
const unsigned long now = millis();
if (now - t_led >= LED_PERIOD_MS) {
t_led += LED_PERIOD_MS;
led_state1=!led_state1; led_state2=!led_state2;
setPin(LED1, led_state1, LED_ACTIVE_HIGH);
setPin(LED2, led_state2, LED_ACTIVE_HIGH);
}
if (ENABLE_HEATER_DEMO && (now - t_heater >= HEATER_PERIOD_MS)) {
t_heater += HEATER_PERIOD_MS;
bed_state=!bed_state; e0_state=!e0_state; e1_state=!e1_state;
setPin(BED, bed_state, HEATER_ACTIVE_HIGH);
setPin(E0, e0_state, HEATER_ACTIVE_HIGH);
setPin(E1, e1_state, HEATER_ACTIVE_HIGH);
}
// Debounced probe read
bool raw = digitalRead(BLT_PROBE_PIN); // HIGH idle, LOW trigger
static unsigned long low_since = 0;
if (!raw) { if (!low_since) low_since = now; }
else low_since = 0;
bool debounced_low = (low_since && (now - low_since >= PROBE_CONFIRM_MS));
setPin(LED1, debounced_low, LED_ACTIVE_HIGH);
if (edgeSeen) edgeSeen=false;
if (now >= mask_until) {
if (debounced_low && !last_reported_low) {
last_reported_low = true;
Serial2.println("[BLT] TRIGGER (debounced)");
} else if (!debounced_low && last_reported_low) {
last_reported_low = false;
Serial2.println("[BLT] RELEASE");
}
}
if (Serial2.available()) {
switch ((char)Serial2.read()) {
case 'd': blt_deploy(); Serial2.println("[BLT] Deploy"); break;
case 's': blt_stow(); Serial2.println("[BLT] Stow"); break;
case 't': blt_self(); Serial2.println("[BLT] Self-test"); break;
case 'r': blt_reset(); Serial2.println("[BLT] Reset"); break;
case 'c': Serial2.println("[BLT] Cycle"); blt_cycle(); break;
case 'p': Serial2.println(raw ? "[BLT] HIGH (idle)" : "[BLT] LOW (TRIGGER)"); break;
case 'h': default: Serial2.println("d s t r c p h"); break;
}
}
}