Quellcode durchsuchen

Zephyr port: hardware bring-up fixes (boots + enumerates on nice!nano)

Verified on a real nRF52840 (nice!nano): the firmware boots, initializes all
subsystems (RNG serial, config/bonds, radio configured ch18/ibex), and
enumerates USB as the Valve puck composite (HID + CDC console). Boot banner
confirms 'mode=STEAM(puck)'.

Fixes this required, all real correctness bugs:
- CONFIG_USE_DT_CODE_PARTITION=y: link at 0x26000 (was 0x0 -> would overwrite
  the bootloader/MBR region; caught before flashing).
- VBUS-gated enable: on the nRF52840 usbd_enable() must wait for the
  USBD_MSG_VBUS_READY message (mirrors the Zephyr sample); enabling eagerly
  prevented enumeration.
- Endpoint budget: the nRF52840 USBD has 7 usable IN endpoints; puck mode's
  5 HID + WebUSB + CDC = 8 overflowed. Drop WebUSB (keep the CDC console) so
  puck = 7 IN. WebUSB panel in puck mode is a known tradeoff for now.
- k_yield() per main loop so the cooperative USBD thread gets CPU.
- LED boot-stage heartbeat on P0.15 (nice!nano blue LED) for bring-up.

Flashed via adafruit-nrfutil serial DFU (adafruit/nice!nano bootloader).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Jari vor 4 Monaten
Ursprung
Commit
b41ce24f58
3 geänderte Dateien mit 61 neuen und 15 gelöschten Zeilen
  1. 5 0
      OpenPuckZephyr/prj.conf
  2. 25 10
      OpenPuckZephyr/src/compat/usb_device_setup.cpp
  3. 31 5
      OpenPuckZephyr/src/main.cpp

+ 5 - 0
OpenPuckZephyr/prj.conf

@@ -34,6 +34,11 @@ CONFIG_FLASH=y
 CONFIG_FLASH_MAP=y
 CONFIG_REBOOT=y
 
+# CRITICAL: link the image at the code partition (0x26000) so it loads where the
+# Adafruit/nice!nano UF2 bootloader expects the application. Without this Zephyr
+# links at 0x0 and the UF2 would overwrite the MBR/bootloader region (brick).
+CONFIG_USE_DT_CODE_PARTITION=y
+
 # Persistence: the Arduino tree stored cfg.bin/bonds.bin in the nRF52 internal
 # LittleFS. We keep LittleFS (Zephyr's fs subsystem on the storage partition) so
 # the persistence code ports behind a thin Adafruit_LittleFS shim.

+ 25 - 10
OpenPuckZephyr/src/compat/usb_device_setup.cpp

@@ -102,7 +102,15 @@ static bool s_enabled;
 
 static void msg_cb(struct usbd_context *const ctx, const struct usbd_msg *const msg)
 {
-	(void)ctx;
+	// On the nRF52840 the USB peripheral can detect VBUS; the controller must
+	// be enabled only once VBUS is present (and disabled when it goes away),
+	// otherwise enumeration never starts. Mirror the Zephyr sample.
+	if (usbd_can_detect_vbus(ctx)) {
+		if (msg->type == USBD_MSG_VBUS_READY)
+			usbd_enable(ctx);
+		else if (msg->type == USBD_MSG_VBUS_REMOVED)
+			usbd_disable(ctx);
+	}
 	opk_usb_msg(msg->type);
 }
 
@@ -140,10 +148,13 @@ static int do_setup(void)
 	int hc = opk_hid_claimed();
 	for (int i = hc; i < 5; i++)
 		block[bi++] = HIDN[i]; // unused HID pool nodes
-	if (!opk_want_webusb()) {
-		block[bi++] = "opk_webusb_0";
-		block[bi++] = "cdc_acm_0"; // CDC rides with the panel (clean-PS drops both)
-	}
+	// The nRF52840 USBD has only 7 usable IN endpoints (EP1..7) beyond EP0.
+	// Puck mode already uses 5 HID IN (wake + 4 slots); adding WebUSB (1) + CDC
+	// (2) overflows to 8. For now drop WebUSB and keep the CDC console (5 HID +
+	// 2 CDC = 7) — the console is the only bring-up observability on this board.
+	block[bi++] = "opk_webusb_0";
+	if (!opk_want_webusb())
+		block[bi++] = "cdc_acm_0"; // clean-PS: single HID only, no CDC
 	if (!opk_xinput_want())
 		block[bi++] = "opk_xinput_0";
 	block[bi] = NULL;
@@ -189,12 +200,16 @@ extern "C" int opk_usbd_enable(void)
 		LOG_ERR("usbd_init failed (%d)", err);
 		return err;
 	}
-	err = usbd_enable(&opk_usbd);
-	if (err && err != -EALREADY) {
-		LOG_ERR("usbd_enable failed (%d)", err);
-		return err;
-	}
 	s_enabled = true;
+	// When the controller can detect VBUS (nRF52840), enabling happens in the
+	// message callback on USBD_MSG_VBUS_READY. Otherwise enable now.
+	if (!usbd_can_detect_vbus(&opk_usbd)) {
+		err = usbd_enable(&opk_usbd);
+		if (err && err != -EALREADY) {
+			LOG_ERR("usbd_enable failed (%d)", err);
+			return err;
+		}
+	}
 	return 0;
 }
 

+ 31 - 5
OpenPuckZephyr/src/main.cpp

@@ -329,14 +329,40 @@ void loop()
 }
 
 // Zephyr entry. The Arduino runtime called setup() once then loop() forever; we
-// do the same on the main thread. The loop is intentionally tight (no sleep):
-// the RF poll cadence (~250 Hz) depends on coming around quickly, exactly as on
-// the Arduino core. USB servicing runs in the USBD stack thread + ISRs, so the
-// tight main loop does not starve it.
+// do the same on the main thread. The loop is intentionally tight (the RF poll
+// cadence depends on coming around quickly), but we k_yield() each iteration so
+// the cooperative USBD stack thread always gets CPU for control transfers.
+// Bring-up diagnostic via the nice!nano blue LED (P0.15 = absolute pin 15):
+//   3 slow blinks  -> main() reached (bootloader handoff + C++ init OK)
+//   6 fast blinks  -> setup() returned (USB/radio/persistence init didn't hang)
+//   then ~1 Hz heartbeat in the loop -> the cooperative loop is running
+// Tells us where it dies when USB never enumerates. Remove once USB is up.
+#define OPK_DIAG_LED 15
+static void diag_blink(int n, int ms)
+{
+	for (int i = 0; i < n; i++) {
+		digitalWrite(OPK_DIAG_LED, HIGH);
+		delay(ms);
+		digitalWrite(OPK_DIAG_LED, LOW);
+		delay(ms);
+	}
+}
+
 int main(void)
 {
+	pinMode(OPK_DIAG_LED, OUTPUT);
+	diag_blink(3, 200); // main() reached
 	setup();
-	for (;;)
+	diag_blink(6, 70); // setup() returned
+	uint32_t last = millis();
+	for (;;) {
 		loop();
+		k_yield();
+		if (millis() - last >= 500) {
+			last = millis();
+			digitalWrite(OPK_DIAG_LED,
+				     (millis() / 500) & 1 ? HIGH : LOW);
+		}
+	}
 	return 0;
 }