mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-08-28 05:24:22 +00:00
Add shared SPI bus mutex for SD card, display, and LoRa coexistence
The T-Deck Plus shares HSPI across the display (CS=12), LoRa (CS=9), and SD card (CS=39). Previously SD logging was disabled because SD.begin() reconfigured the SPI bus and blanked the display. This introduces a FreeRTOS mutex created in main.cpp and injected into Display, SX1262Interface, and a new SDAccess class so all three peripherals serialize their SPI transactions safely. - Add SDAccess class wrapping SD.begin() and file ops with mutex - Add set_spi_mutex() to Display and SX1262Interface - Wrap Display flush, fill, draw, and power ops in mutex - Refactor SDLogger to use SDAccess mutex instead of owning SD.begin() - Wire up mutex creation and injection order in setup() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
d9411fb4bb
commit
d03f0b308f
@@ -12,9 +12,17 @@
|
||||
using namespace RNS;
|
||||
|
||||
#ifdef ARDUINO
|
||||
// Static members for SPI mutex (shared with display)
|
||||
// Static members for SPI mutex (shared with display and SD card)
|
||||
SemaphoreHandle_t SX1262Interface::_spi_mutex = nullptr;
|
||||
bool SX1262Interface::_mutex_initialized = false;
|
||||
|
||||
void SX1262Interface::set_spi_mutex(SemaphoreHandle_t mutex) {
|
||||
_spi_mutex = mutex;
|
||||
_mutex_initialized = (mutex != nullptr);
|
||||
if (_mutex_initialized) {
|
||||
DEBUG("SX1262Interface: Using external SPI mutex");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SX1262Interface::SX1262Interface(const char* name) : InterfaceImpl(name) {
|
||||
@@ -58,15 +66,15 @@ bool SX1262Interface::start() {
|
||||
INFO(" CR: 4/" + std::to_string(_config.coding_rate));
|
||||
INFO(" TX Power: " + std::to_string(_config.tx_power) + " dBm");
|
||||
|
||||
// Initialize SPI mutex if not already done
|
||||
// Use external mutex if provided, otherwise create our own (fallback)
|
||||
if (!_mutex_initialized) {
|
||||
WARNING("SX1262Interface: No external SPI mutex set, creating own");
|
||||
_spi_mutex = xSemaphoreCreateMutex();
|
||||
if (_spi_mutex == nullptr) {
|
||||
ERROR("SX1262Interface: Failed to create SPI mutex");
|
||||
return false;
|
||||
}
|
||||
_mutex_initialized = true;
|
||||
DEBUG("SX1262Interface: SPI mutex created");
|
||||
}
|
||||
|
||||
// Acquire SPI mutex
|
||||
|
||||
@@ -50,6 +50,12 @@ public:
|
||||
SX1262Interface(const char* name = "LoRa");
|
||||
virtual ~SX1262Interface();
|
||||
|
||||
/**
|
||||
* Set the shared SPI bus mutex. Call before start().
|
||||
* If set, the interface uses this mutex instead of creating its own.
|
||||
*/
|
||||
static void set_spi_mutex(SemaphoreHandle_t mutex);
|
||||
|
||||
/**
|
||||
* Set configuration before calling start().
|
||||
* Changes take effect on next start().
|
||||
|
||||
@@ -14,12 +14,17 @@ namespace Hardware {
|
||||
namespace TDeck {
|
||||
|
||||
SPIClass* Display::_spi = nullptr;
|
||||
SemaphoreHandle_t Display::_spi_mutex = nullptr;
|
||||
uint8_t Display::_brightness = Disp::BACKLIGHT_DEFAULT;
|
||||
bool Display::_initialized = false;
|
||||
volatile uint32_t Display::_flush_count = 0;
|
||||
volatile uint32_t Display::_last_flush_ms = 0;
|
||||
uint32_t Display::_last_health_log_ms = 0;
|
||||
|
||||
void Display::set_spi_mutex(SemaphoreHandle_t mutex) {
|
||||
_spi_mutex = mutex;
|
||||
}
|
||||
|
||||
bool Display::init() {
|
||||
if (_initialized) {
|
||||
return true;
|
||||
@@ -157,6 +162,9 @@ uint8_t Display::get_brightness() {
|
||||
}
|
||||
|
||||
void Display::set_power(bool on) {
|
||||
if (_spi_mutex && xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(200)) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
if (on) {
|
||||
write_command(Command::DISPON);
|
||||
set_brightness(_brightness);
|
||||
@@ -164,9 +172,13 @@ void Display::set_power(bool on) {
|
||||
set_brightness(0);
|
||||
write_command(Command::DISPOFF);
|
||||
}
|
||||
if (_spi_mutex) xSemaphoreGive(_spi_mutex);
|
||||
}
|
||||
|
||||
void Display::fill_screen(uint16_t color) {
|
||||
if (_spi_mutex && xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(500)) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
set_addr_window(0, 0, Disp::WIDTH - 1, Disp::HEIGHT - 1);
|
||||
|
||||
begin_write();
|
||||
@@ -181,6 +193,7 @@ void Display::fill_screen(uint16_t color) {
|
||||
write_data(color_bytes, 2);
|
||||
}
|
||||
end_write();
|
||||
if (_spi_mutex) xSemaphoreGive(_spi_mutex);
|
||||
}
|
||||
|
||||
void Display::draw_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
|
||||
@@ -188,6 +201,10 @@ void Display::draw_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t col
|
||||
return;
|
||||
}
|
||||
|
||||
if (_spi_mutex && xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(200)) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_addr_window(x, y, x + w - 1, y + h - 1);
|
||||
|
||||
begin_write();
|
||||
@@ -201,9 +218,16 @@ void Display::draw_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t col
|
||||
write_data(color_bytes, 2);
|
||||
}
|
||||
end_write();
|
||||
if (_spi_mutex) xSemaphoreGive(_spi_mutex);
|
||||
}
|
||||
|
||||
void Display::lvgl_flush_cb(lv_disp_drv_t* drv, const lv_area_t* area, lv_color_t* color_p) {
|
||||
if (_spi_mutex && xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(100)) != pdTRUE) {
|
||||
// Skip this frame — LVGL will retry next tick
|
||||
lv_disp_flush_ready(drv);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t start = millis();
|
||||
|
||||
int32_t w = area->x2 - area->x1 + 1;
|
||||
@@ -221,6 +245,8 @@ void Display::lvgl_flush_cb(lv_disp_drv_t* drv, const lv_area_t* area, lv_color_
|
||||
|
||||
end_write();
|
||||
|
||||
if (_spi_mutex) xSemaphoreGive(_spi_mutex);
|
||||
|
||||
_flush_count++;
|
||||
_last_flush_ms = millis();
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include <lvgl.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
namespace Hardware {
|
||||
namespace TDeck {
|
||||
@@ -27,6 +29,13 @@ namespace TDeck {
|
||||
*/
|
||||
class Display {
|
||||
public:
|
||||
/**
|
||||
* Set the shared SPI bus mutex. Call before init().
|
||||
* If not set, Display operates without mutex protection
|
||||
* (safe during early boot before other SPI users exist).
|
||||
*/
|
||||
static void set_spi_mutex(SemaphoreHandle_t mutex);
|
||||
|
||||
/**
|
||||
* Initialize display and LVGL
|
||||
* @return true if initialization successful
|
||||
@@ -131,6 +140,7 @@ private:
|
||||
};
|
||||
|
||||
static SPIClass* _spi;
|
||||
static SemaphoreHandle_t _spi_mutex;
|
||||
static uint8_t _brightness;
|
||||
static bool _initialized;
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright (c) 2024 microReticulum contributors
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "SDAccess.h"
|
||||
|
||||
#ifdef ARDUINO
|
||||
|
||||
#include <Log.h>
|
||||
|
||||
using namespace RNS;
|
||||
|
||||
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) {
|
||||
if (_ready) return true;
|
||||
if (mutex == nullptr) {
|
||||
Serial.println("[SDAccess] ERROR: null mutex");
|
||||
return false;
|
||||
}
|
||||
|
||||
_spi_mutex = mutex;
|
||||
|
||||
// Drive SD CS high before init to avoid bus contention
|
||||
pinMode(SDCard::CS, OUTPUT);
|
||||
digitalWrite(SDCard::CS, HIGH);
|
||||
|
||||
// Acquire mutex for SD.begin() since it reconfigures SPI
|
||||
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);
|
||||
|
||||
bool ok = SD.begin(SDCard::CS, *_sd_spi, SD_SPI_FREQ);
|
||||
|
||||
xSemaphoreGive(_spi_mutex);
|
||||
|
||||
if (!ok) {
|
||||
Serial.println("[SDAccess] SD card mount failed (no card?)");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t cardType = SD.cardType();
|
||||
if (cardType == CARD_NONE) {
|
||||
Serial.println("[SDAccess] No SD card detected");
|
||||
return false;
|
||||
}
|
||||
|
||||
_ready = true;
|
||||
|
||||
const char* type_str = "UNKNOWN";
|
||||
switch (cardType) {
|
||||
case CARD_MMC: type_str = "MMC"; break;
|
||||
case CARD_SD: type_str = "SD"; break;
|
||||
case CARD_SDHC: type_str = "SDHC"; break;
|
||||
default: break;
|
||||
}
|
||||
Serial.printf("[SDAccess] Card type: %s, size: %lluMB\n",
|
||||
type_str, SD.cardSize() / (1024 * 1024));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int SDAccess::read_file(const char* path, uint8_t* buf, size_t max_len) {
|
||||
if (!_ready || !_spi_mutex) return -1;
|
||||
|
||||
if (xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(500)) != pdTRUE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
File f = SD.open(path, FILE_READ);
|
||||
if (!f) {
|
||||
xSemaphoreGive(_spi_mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int bytes_read = f.read(buf, max_len);
|
||||
f.close();
|
||||
|
||||
xSemaphoreGive(_spi_mutex);
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
bool SDAccess::file_exists(const char* path) {
|
||||
if (!_ready || !_spi_mutex) return false;
|
||||
|
||||
if (xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(500)) != pdTRUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool exists = SD.exists(path);
|
||||
|
||||
xSemaphoreGive(_spi_mutex);
|
||||
return exists;
|
||||
}
|
||||
|
||||
bool SDAccess::acquire_bus(uint32_t timeout_ms) {
|
||||
if (!_spi_mutex) return false;
|
||||
return xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(timeout_ms)) == pdTRUE;
|
||||
}
|
||||
|
||||
void SDAccess::release_bus() {
|
||||
if (_spi_mutex) {
|
||||
xSemaphoreGive(_spi_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TDeck
|
||||
} // namespace Hardware
|
||||
|
||||
#endif // ARDUINO
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2024 microReticulum contributors
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef HARDWARE_TDECK_SDACCESS_H
|
||||
#define HARDWARE_TDECK_SDACCESS_H
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#include <SD.h>
|
||||
#include <SPI.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
namespace Hardware {
|
||||
namespace TDeck {
|
||||
|
||||
/**
|
||||
* Mutex-protected SD card access for shared SPI bus.
|
||||
*
|
||||
* The T-Deck Plus shares HSPI (SCK=40, MOSI=41, MISO=38) across
|
||||
* display (CS=12), LoRa (CS=9), and SD card (CS=39). All SPI
|
||||
* operations must be serialized via a shared FreeRTOS mutex.
|
||||
*
|
||||
* SDAccess wraps SD.begin() and all file operations in the mutex
|
||||
* so callers don't need to manage bus arbitration themselves.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param mutex Shared SPI bus mutex (created in main.cpp)
|
||||
* @return true if SD card mounted successfully
|
||||
*/
|
||||
static bool init(SemaphoreHandle_t mutex);
|
||||
|
||||
/** Check if SD card is mounted and ready */
|
||||
static bool is_ready() { return _ready; }
|
||||
|
||||
/** Get the shared SPI mutex (for SDLogger to wrap its own operations) */
|
||||
static SemaphoreHandle_t get_mutex() { return _spi_mutex; }
|
||||
|
||||
/**
|
||||
* Read an entire file into a buffer with mutex protection.
|
||||
* @return bytes read, or -1 on error
|
||||
*/
|
||||
static int read_file(const char* path, uint8_t* buf, size_t max_len);
|
||||
|
||||
/** Check if a file exists (with mutex) */
|
||||
static bool file_exists(const char* path);
|
||||
|
||||
/**
|
||||
* Acquire the SPI bus mutex for streaming operations.
|
||||
* Caller MUST call release_bus() when done.
|
||||
* @param timeout_ms Max wait time
|
||||
* @return true if mutex acquired
|
||||
*/
|
||||
static bool acquire_bus(uint32_t timeout_ms = 500);
|
||||
|
||||
/** Release the SPI bus mutex after streaming operations */
|
||||
static void release_bus();
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
} // namespace TDeck
|
||||
} // namespace Hardware
|
||||
|
||||
#endif // ARDUINO
|
||||
#endif // HARDWARE_TDECK_SDACCESS_H
|
||||
@@ -25,24 +25,21 @@ bool SDLogger::init() {
|
||||
}
|
||||
_initialized = true;
|
||||
|
||||
// Initialize SD card on shared SPI bus
|
||||
// Note: SPI should already be initialized by display
|
||||
if (!SD.begin(SDCard::CS)) {
|
||||
Serial.println("[SDLogger] SD card mount failed");
|
||||
// SDAccess must already be initialized (handles SD.begin with mutex)
|
||||
if (!SDAccess::is_ready()) {
|
||||
Serial.println("[SDLogger] SDAccess not ready, skipping SD logging");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check card type
|
||||
uint8_t cardType = SD.cardType();
|
||||
if (cardType == CARD_NONE) {
|
||||
Serial.println("[SDLogger] No SD card detected");
|
||||
// Open log file with mutex protection
|
||||
if (!SDAccess::acquire_bus(1000)) {
|
||||
Serial.println("[SDLogger] Failed to acquire SPI mutex for log file open");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open or create log file
|
||||
// Use append mode to preserve history across reboots
|
||||
_logFile = SD.open(CURRENT_LOG, FILE_APPEND);
|
||||
if (!_logFile) {
|
||||
SDAccess::release_bus();
|
||||
Serial.println("[SDLogger] Failed to open log file");
|
||||
return false;
|
||||
}
|
||||
@@ -55,6 +52,8 @@ bool SDLogger::init() {
|
||||
_logFile.println("========================================\n");
|
||||
_logFile.flush();
|
||||
|
||||
SDAccess::release_bus();
|
||||
|
||||
_bytes_written = 0;
|
||||
_last_flush = millis();
|
||||
_line_count = 0;
|
||||
@@ -64,7 +63,6 @@ bool SDLogger::init() {
|
||||
RNS::setLogCallback(logCallback);
|
||||
|
||||
Serial.println("[SDLogger] SD card logging active");
|
||||
Serial.printf("[SDLogger] Card size: %lluMB\n", SD.cardSize() / (1024 * 1024));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -85,6 +83,10 @@ void SDLogger::logCallback(const char* msg, RNS::LogLevel level) {
|
||||
}
|
||||
|
||||
void SDLogger::writeToFile(const char* msg, RNS::LogLevel level) {
|
||||
if (!SDAccess::acquire_bus(50)) {
|
||||
return; // Skip this log line rather than block the caller
|
||||
}
|
||||
|
||||
// Format: timestamp [LEVEL] message
|
||||
int written = _logFile.printf("%s [%s] %s\n",
|
||||
RNS::getTimeString(),
|
||||
@@ -118,28 +120,34 @@ void SDLogger::writeToFile(const char* msg, RNS::LogLevel level) {
|
||||
_line_count = 0;
|
||||
}
|
||||
|
||||
// Check if we need to rotate
|
||||
// Check if we need to rotate (still holding mutex)
|
||||
if (_bytes_written >= MAX_LOG_SIZE) {
|
||||
rotateIfNeeded();
|
||||
}
|
||||
|
||||
SDAccess::release_bus();
|
||||
}
|
||||
|
||||
void SDLogger::flush() {
|
||||
if (_active && _logFile) {
|
||||
if (!SDAccess::acquire_bus(100)) return;
|
||||
_logFile.flush();
|
||||
_last_flush = millis();
|
||||
_line_count = 0;
|
||||
SDAccess::release_bus();
|
||||
}
|
||||
}
|
||||
|
||||
void SDLogger::marker(const char* msg) {
|
||||
if (_active && _logFile) {
|
||||
if (!SDAccess::acquire_bus(200)) return;
|
||||
_logFile.println("----------------------------------------");
|
||||
_logFile.printf(">>> MARKER: %s <<<\n", msg);
|
||||
_logFile.printf(">>> Heap: %lu / Min: %lu <<<\n",
|
||||
ESP.getFreeHeap(), ESP.getMinFreeHeap());
|
||||
_logFile.println("----------------------------------------");
|
||||
_logFile.flush();
|
||||
SDAccess::release_bus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,9 +183,12 @@ void SDLogger::rotateIfNeeded() {
|
||||
|
||||
void SDLogger::close() {
|
||||
if (_active && _logFile) {
|
||||
_logFile.println("\n=== LOG CLOSED CLEANLY ===");
|
||||
_logFile.flush();
|
||||
_logFile.close();
|
||||
if (SDAccess::acquire_bus(500)) {
|
||||
_logFile.println("\n=== LOG CLOSED CLEANLY ===");
|
||||
_logFile.flush();
|
||||
_logFile.close();
|
||||
SDAccess::release_bus();
|
||||
}
|
||||
_active = false;
|
||||
}
|
||||
// Restore default logging
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#define HARDWARE_TDECK_SDLOGGER_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "SDAccess.h"
|
||||
#include <Log.h>
|
||||
|
||||
#ifdef ARDUINO
|
||||
@@ -29,10 +30,10 @@ namespace TDeck {
|
||||
class SDLogger {
|
||||
public:
|
||||
/**
|
||||
* Initialize SD card and set up logging callback.
|
||||
* Must be called after SPI bus is initialized (after display init).
|
||||
* Initialize SD logging. Must be called after SDAccess::init().
|
||||
* Uses the shared SPI mutex from SDAccess for all file operations.
|
||||
*
|
||||
* @return true if SD card mounted and logging active
|
||||
* @return true if SD card accessible and logging active
|
||||
*/
|
||||
static bool init();
|
||||
|
||||
|
||||
+31
-4
@@ -63,7 +63,8 @@
|
||||
// Logging
|
||||
#include <Log.h>
|
||||
|
||||
// SD Card logging for crash debugging
|
||||
// SD Card access and logging
|
||||
#include <Hardware/TDeck/SDAccess.h>
|
||||
#include <Hardware/TDeck/SDLogger.h>
|
||||
|
||||
// OTA flashing
|
||||
@@ -1276,19 +1277,45 @@ void setup() {
|
||||
}
|
||||
}
|
||||
|
||||
// Create shared SPI bus mutex (display, LoRa, SD card all share HSPI)
|
||||
SemaphoreHandle_t spi_mutex = xSemaphoreCreateMutex();
|
||||
if (!spi_mutex) {
|
||||
ERROR("Failed to create SPI bus mutex!");
|
||||
}
|
||||
|
||||
// Set SPI mutex on Display before init (null-safe if mutex creation failed)
|
||||
Hardware::TDeck::Display::set_spi_mutex(spi_mutex);
|
||||
|
||||
// Initialize LVGL and hardware drivers
|
||||
BOOT_PROFILE_START("lvgl");
|
||||
setup_lvgl_and_ui();
|
||||
BOOT_PROFILE_END("lvgl");
|
||||
|
||||
// NOTE: SD card logging disabled - shares SPI with display and causes blank screen
|
||||
// TODO: Need to reinitialize display SPI after SD.begin() or use separate bus
|
||||
// Set SPI mutex on LoRa interface (before setup_reticulum creates it)
|
||||
if (spi_mutex) {
|
||||
SX1262Interface::set_spi_mutex(spi_mutex);
|
||||
}
|
||||
|
||||
// Initialize Reticulum
|
||||
// Initialize Reticulum (includes LoRa on shared SPI bus)
|
||||
BOOT_PROFILE_START("reticulum");
|
||||
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?)");
|
||||
}
|
||||
}
|
||||
BOOT_PROFILE_END("sd_card");
|
||||
|
||||
// Initialize LXMF
|
||||
BOOT_PROFILE_START("lxmf");
|
||||
setup_lxmf();
|
||||
|
||||
Reference in New Issue
Block a user