fix: resolve ESP32-S3 runtime crash, match official esp-hal examples
Root causes found and fixed: - USB OTG init stole the PHY from USB-Serial-JTAG (the only USB on V4) - executor-thread feature conflicted with esp-hal-embassy's executor - Linker flags needed -Wl, prefix for GCC-to-LD passthrough - Missing exception-handler feature on esp-backtrace - Missing alloc in build-std - Missing LTO and codegen-units=1 in release profile - Vext on V4 is active LOW (datasheet correct, Arduino lib was wrong) Now matches the official esp-hal v0.22.0 examples exactly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
8 files changed
+58
-70
No files matched your search
+12
-23
@@ -21,7 +21,8 @@ type Iv = GenericSx126xInterfaceVariant<Output<'static>, Input<'static>>;
|
||||
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, SpiBus, Nss>;
|
||||
pub type RadioDriver = Sx126x<RadioSpiDevice, Iv, Sx1262>;
|
||||
|
||||
pub type UsbDriver = esp_hal::otg_fs::asynch::Driver<'static>;
|
||||
// TODO: V3 has CP2102 on UART0 — USB OTG on GPIO19/20 goes nowhere
|
||||
pub type UsbDriver = ();
|
||||
|
||||
pub type DisplayI2c = I2c<'static, esp_hal::Async>;
|
||||
|
||||
@@ -48,22 +49,20 @@ pub struct Board {
|
||||
|
||||
impl Board {
|
||||
pub fn init() -> Self {
|
||||
let mut p = esp_hal::init(esp_hal::Config::default());
|
||||
|
||||
let timg0 = TimerGroup::new(core::mem::replace(
|
||||
&mut p.TIMG0,
|
||||
unsafe { esp_hal::peripherals::TIMG0::steal() },
|
||||
));
|
||||
esp_hal_embassy::init(timg0.timer0);
|
||||
|
||||
let p = esp_hal::init(esp_hal::Config::default());
|
||||
Self { p }
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (RadioParts, UsbParts, Option<DisplayParts>) {
|
||||
let p = self.p;
|
||||
|
||||
// ── Embassy time driver ────────────────────────────────────
|
||||
let timg0 = TimerGroup::new(p.TIMG0);
|
||||
esp_hal_embassy::init(timg0.timer0);
|
||||
|
||||
// ── Vext power: GPIO21, active LOW to enable OLED ──────────
|
||||
let _vext = Output::new(p.GPIO21, Level::Low);
|
||||
let vext = Output::new(p.GPIO21, Level::Low);
|
||||
core::mem::forget(vext);
|
||||
|
||||
// ── DMA for SPI ────────────────────────────────────────────
|
||||
let dma = Dma::new(p.DMA);
|
||||
@@ -112,20 +111,10 @@ impl Board {
|
||||
delay: Delay,
|
||||
};
|
||||
|
||||
// ── USB (native ESP32-S3 OTG) ─────────────────────────────
|
||||
let usb_inst = esp_hal::otg_fs::Usb::new(p.USB0, p.GPIO20, p.GPIO19);
|
||||
static EP_OUT_BUF: StaticCell<[u8; 1024]> = StaticCell::new();
|
||||
let ep_out_buf = EP_OUT_BUF.init([0u8; 1024]);
|
||||
let usb = UsbParts {
|
||||
driver: esp_hal::otg_fs::asynch::Driver::new(
|
||||
usb_inst,
|
||||
ep_out_buf,
|
||||
esp_hal::otg_fs::asynch::Config::default(),
|
||||
),
|
||||
};
|
||||
// ── USB (placeholder — V3 uses CP2102 on UART0) ───────────
|
||||
let usb = UsbParts { driver: () };
|
||||
|
||||
// ── Display (optional SSD1306 OLED on I2C, 0x3C) ──────────
|
||||
// Always provide I2C; display_task detects presence via SSD1306 init.
|
||||
// ── Display (SSD1306 OLED on I2C, 0x3C) ───────────────────
|
||||
let i2c = I2c::new(p.I2C0, I2cConfig::default())
|
||||
.with_sda(p.GPIO17)
|
||||
.with_scl(p.GPIO18)
|
||||
|
||||
+18
-26
@@ -21,7 +21,8 @@ type Iv = GenericSx126xInterfaceVariant<Output<'static>, Input<'static>>;
|
||||
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, SpiBus, Nss>;
|
||||
pub type RadioDriver = Sx126x<RadioSpiDevice, Iv, Sx1262>;
|
||||
|
||||
pub type UsbDriver = esp_hal::otg_fs::asynch::Driver<'static>;
|
||||
// TODO: USB CDC-ACM (UsbSerialJtag for dev, or full OTG stack for production)
|
||||
pub type UsbDriver = ();
|
||||
|
||||
pub type DisplayI2c = I2c<'static, esp_hal::Async>;
|
||||
|
||||
@@ -48,23 +49,21 @@ pub struct Board {
|
||||
|
||||
impl Board {
|
||||
pub fn init() -> Self {
|
||||
let mut p = esp_hal::init(esp_hal::Config::default());
|
||||
|
||||
// Init embassy time driver before consuming peripherals
|
||||
let timg0 = TimerGroup::new(core::mem::replace(
|
||||
&mut p.TIMG0,
|
||||
unsafe { esp_hal::peripherals::TIMG0::steal() },
|
||||
));
|
||||
esp_hal_embassy::init(timg0.timer0);
|
||||
|
||||
let p = esp_hal::init(esp_hal::Config::default());
|
||||
Self { p }
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (RadioParts, UsbParts, Option<DisplayParts>) {
|
||||
let p = self.p;
|
||||
|
||||
// ── Vext power: GPIO36, active HIGH to enable OLED ─────────
|
||||
let _vext = Output::new(p.GPIO36, Level::High);
|
||||
// ── Embassy time driver ────────────────────────────────────
|
||||
let timg0 = TimerGroup::new(p.TIMG0);
|
||||
esp_hal_embassy::init(timg0.timer0);
|
||||
|
||||
// ── Vext power: GPIO36 ─────────────────────────────────────
|
||||
// V4 datasheet says "pull low to enable Vext"
|
||||
let vext = Output::new(p.GPIO36, Level::Low);
|
||||
core::mem::forget(vext);
|
||||
|
||||
// ── DMA for SPI ────────────────────────────────────────────
|
||||
let dma = Dma::new(p.DMA);
|
||||
@@ -113,24 +112,17 @@ impl Board {
|
||||
delay: Delay,
|
||||
};
|
||||
|
||||
// ── USB (native ESP32-S3 OTG) ─────────────────────────────
|
||||
let usb_inst = esp_hal::otg_fs::Usb::new(p.USB0, p.GPIO20, p.GPIO19);
|
||||
static EP_OUT_BUF: StaticCell<[u8; 1024]> = StaticCell::new();
|
||||
let ep_out_buf = EP_OUT_BUF.init([0u8; 1024]);
|
||||
let usb = UsbParts {
|
||||
driver: esp_hal::otg_fs::asynch::Driver::new(
|
||||
usb_inst,
|
||||
ep_out_buf,
|
||||
esp_hal::otg_fs::asynch::Config::default(),
|
||||
),
|
||||
};
|
||||
// ── USB (placeholder) ──────────────────────────────────────
|
||||
let usb = UsbParts { driver: () };
|
||||
|
||||
// ── Display (optional SSD1315 OLED on I2C, 0x3C) ──────────
|
||||
// Reset display controller via GPIO21
|
||||
// ── Display (SSD1315 OLED on I2C, 0x3C) ───────────────────
|
||||
// Reset display controller
|
||||
let mut display_rst = Output::new(p.GPIO21, Level::Low);
|
||||
esp_hal::delay::Delay::new().delay_millis(10);
|
||||
display_rst.set_high();
|
||||
esp_hal::delay::Delay::new().delay_millis(10);
|
||||
core::mem::forget(display_rst);
|
||||
|
||||
// Always provide I2C; display_task detects presence via SSD1306 init.
|
||||
let i2c = I2c::new(p.I2C0, I2cConfig::default())
|
||||
.with_sda(p.GPIO17)
|
||||
.with_scl(p.GPIO18)
|
||||
|
||||
Reference in new issue
Block a user