diff --git a/lib/tdeck_ui/Hardware/TDeck/Display.cpp b/lib/tdeck_ui/Hardware/TDeck/Display.cpp index c338b5e5..9091ea01 100644 --- a/lib/tdeck_ui/Hardware/TDeck/Display.cpp +++ b/lib/tdeck_ui/Hardware/TDeck/Display.cpp @@ -88,7 +88,9 @@ bool Display::init_hardware_only() { // Initialize SPI _spi = new SPIClass(HSPI); - _spi->begin(Pin::DISPLAY_SCK, -1, Pin::DISPLAY_MOSI, Pin::DISPLAY_CS); + // Include MISO (pin 38) even though display is write-only — SD card and + // LoRa share this SPI instance and need MISO for read operations. + _spi->begin(Pin::DISPLAY_SCK, Radio::SPI_MISO, Pin::DISPLAY_MOSI, Pin::DISPLAY_CS); // Configure CS and DC pins pinMode(Pin::DISPLAY_CS, OUTPUT); diff --git a/lib/tdeck_ui/Hardware/TDeck/Display.h b/lib/tdeck_ui/Hardware/TDeck/Display.h index 8aeafa45..9dd0265e 100644 --- a/lib/tdeck_ui/Hardware/TDeck/Display.h +++ b/lib/tdeck_ui/Hardware/TDeck/Display.h @@ -36,6 +36,9 @@ public: */ static void set_spi_mutex(SemaphoreHandle_t mutex); + /** Get the SPI instance (for sharing with SD card) */ + static SPIClass* get_spi() { return _spi; } + /** * Initialize display and LVGL * @return true if initialization successful diff --git a/lib/tdeck_ui/Hardware/TDeck/SDAccess.cpp b/lib/tdeck_ui/Hardware/TDeck/SDAccess.cpp index bc00c93c..d3c6a64d 100644 --- a/lib/tdeck_ui/Hardware/TDeck/SDAccess.cpp +++ b/lib/tdeck_ui/Hardware/TDeck/SDAccess.cpp @@ -13,7 +13,6 @@ namespace Hardware { namespace TDeck { SemaphoreHandle_t SDAccess::_spi_mutex = nullptr; -SPIClass* SDAccess::_sd_spi = nullptr; bool SDAccess::_ready = false; bool SDAccess::init(SemaphoreHandle_t mutex) { @@ -25,22 +24,24 @@ bool SDAccess::init(SemaphoreHandle_t mutex) { _spi_mutex = mutex; - // Drive SD CS high before init to avoid bus contention + // Drive ALL SPI CS lines high to deselect other devices on the bus. + pinMode(Pin::DISPLAY_CS, OUTPUT); + digitalWrite(Pin::DISPLAY_CS, HIGH); + pinMode(Radio::LORA_CS, OUTPUT); + digitalWrite(Radio::LORA_CS, HIGH); pinMode(SDCard::CS, OUTPUT); digitalWrite(SDCard::CS, HIGH); - // Acquire mutex for SD.begin() since it reconfigures SPI + // Acquire mutex for SD.begin() if (xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(2000)) != pdTRUE) { Serial.println("[SDAccess] Failed to acquire SPI mutex for init"); return false; } - // Create SPIClass on HSPI — attaches to same hardware peripheral - // as display and LoRa, but SD.begin() needs its own instance - _sd_spi = new SPIClass(HSPI); - _sd_spi->begin(Pin::DISPLAY_SCK, Radio::SPI_MISO, Pin::DISPLAY_MOSI, SDCard::CS); + // SD card init: use global SPI (FSPI) before Display claims HSPI + SPI.begin(Pin::DISPLAY_SCK, Radio::SPI_MISO, Pin::DISPLAY_MOSI); - bool ok = SD.begin(SDCard::CS, *_sd_spi, SD_SPI_FREQ); + bool ok = SD.begin(SDCard::CS, SPI, SD_SPI_FREQ); xSemaphoreGive(_spi_mutex); diff --git a/lib/tdeck_ui/Hardware/TDeck/SDAccess.h b/lib/tdeck_ui/Hardware/TDeck/SDAccess.h index 1852c1bf..d9dd97dd 100644 --- a/lib/tdeck_ui/Hardware/TDeck/SDAccess.h +++ b/lib/tdeck_ui/Hardware/TDeck/SDAccess.h @@ -29,9 +29,9 @@ namespace TDeck { class SDAccess { public: /** - * Initialize SD card on the shared HSPI bus. - * Creates its own SPIClass(HSPI) attached to the same hardware - * peripheral, then calls SD.begin() with that instance. + * Initialize SD card on the shared SPI bus. + * Must be called BEFORE Display::init() to avoid SPI peripheral conflicts. + * Uses the global SPI (FSPI) instance matching LilyGo's reference code. * * @param mutex Shared SPI bus mutex (created in main.cpp) * @return true if SD card mounted successfully @@ -66,10 +66,9 @@ public: private: static SemaphoreHandle_t _spi_mutex; - static SPIClass* _sd_spi; static bool _ready; - static constexpr uint32_t SD_SPI_FREQ = 20000000; // 20MHz for SD card + static constexpr uint32_t SD_SPI_FREQ = 800000; // 800kHz (matches LilyGo example) }; } // namespace TDeck diff --git a/src/main.cpp b/src/main.cpp index 65120523..bbdfa67f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1277,12 +1277,24 @@ void setup() { } } - // Create shared SPI bus mutex (display, LoRa, SD card all share HSPI) + // Create shared SPI bus mutex (display, LoRa, SD card all share SPI) SemaphoreHandle_t spi_mutex = xSemaphoreCreateMutex(); if (!spi_mutex) { ERROR("Failed to create SPI bus mutex!"); } + // Try SD card FIRST, before display claims pins — matches LilyGo init order. + // Uses global SPI (FSPI) with no competing peripheral on the bus. + BOOT_PROFILE_START("sd_card"); + if (spi_mutex) { + if (Hardware::TDeck::SDAccess::init(spi_mutex)) { + INFO("SD card initialized on shared SPI bus"); + } else { + INFO("SD card not available (no card inserted?)"); + } + } + BOOT_PROFILE_END("sd_card"); + // Set SPI mutex on Display before init (null-safe if mutex creation failed) Hardware::TDeck::Display::set_spi_mutex(spi_mutex); @@ -1301,20 +1313,12 @@ void setup() { setup_reticulum(); BOOT_PROFILE_END("reticulum"); - // Initialize SD card on shared SPI bus (after display + LoRa) - BOOT_PROFILE_START("sd_card"); - if (spi_mutex) { - if (Hardware::TDeck::SDAccess::init(spi_mutex)) { - INFO("SD card initialized on shared SPI bus"); - // Initialize SD logging now that SDAccess is ready - if (Hardware::TDeck::SDLogger::init()) { - INFO("SD card logging active"); - } - } else { - INFO("SD card not available (no card inserted?)"); + // Initialize SD logging (SDAccess already initialized above) + if (Hardware::TDeck::SDAccess::is_ready()) { + if (Hardware::TDeck::SDLogger::init()) { + INFO("SD card logging active"); } } - BOOT_PROFILE_END("sd_card"); // Initialize LXMF BOOT_PROFILE_START("lxmf");