Optimize performance: 4-bit SD_MMC, write-only SPI, and hardware byte swapping

This commit is contained in:
drjones
2026-06-30 01:18:01 -07:00
parent e04ee7cfe4
commit 4fa200d649
4 changed files with 29 additions and 32 deletions

View File

@@ -2,21 +2,25 @@
This repository contains a responsive, dual-core firmware application developed for the **Waveshare ESP32-S3-LCD-1.47** development board.
The application reads PNG images and uncompressed PCM WAV audio files from a microSD card, renders the images on the onboard ST7789 LCD display, and plays matching soundbites over an external I2S audio DAC when the onboard button is pressed.
The application reads PNG images and compressed MP3 / uncompressed WAV audio files from a microSD card, renders the images on the onboard ST7789 LCD display, and plays matching soundbites over an external I2S audio DAC.
---
## 🚀 Key Features
* **Alpha-Numeric Media Pairing:** Scans the root of the microSD card for PNG and WAV files, sorting and matching them alphabetically (e.g., `01_image.png` pairs with `01_sound.wav`).
* **Background Multi-Threaded Audio:** Spawns a dedicated FreeRTOS task pinned to **Core 0** for I2S WAV streaming, preventing audio stutter and keeping the UI on **Core 1** fully responsive.
* **Dual Operation Modes:**
* **Manual Mode (Default):** Advances slides and triggers soundbites on single-presses.
* **Autoplay (Slideshow) Mode:** Long-pressing the button (>1.5s) toggles a hands-free slideshow. It automatically advances to the next slide 5 seconds *after* the previous audio track has finished playing.
* **Alpha-Numeric Media Pairing:** Scans the root of the microSD card for PNG images and audio files (`.wav` or `.mp3`), sorting and matching them alphabetically (e.g., `01_image.png` pairs with `01_sound.mp3`).
* **Dual-Core Software Decoding:** Spawns a background task pinned to **Core 0** running the `ESP32-audioI2S` decoding loop. This prevents audio dropouts or stutters during heavy display updates on **Core 1**.
* **Interactive Multi-Click Gestures:** You can control slide progression, audio status, and volume from the board's single physical button:
* *Single Click:* Next photo/audio pair.
* *Double Click:* Toggle Pause / Resume audio playback.
* *Triple Click:* Previous photo/audio pair.
* *Quadruple Click:* Cycle volume through levels 4, 8, 12, 16, and 20 (on a 0-21 scale).
* *Long Press (>1.5s):* Toggle Autoplay slideshow mode.
* **WS2812 RGB LED Status Feedback:** Utilizes the onboard RGB LED to output real-time visual feedback:
* 🔵 **Solid Blue:** Idle, waiting for manual trigger.
* 🟢 **Pulsing Green:** Audio is playing.
* 🟣 **Pulsing Purple:** Autoplay/Slideshow mode is active.
* ⚪ **White Flashing:** Confirms a volume change has been triggered.
* 🔴 **Blinking Red:** Error state (microSD card failed to initialize, or no media files were found).
---
@@ -43,11 +47,11 @@ Configure the pin connections as follows:
```text
├── sound_picture_app/ # Custom Application Source
│ ├── sound_picture_app.ino # Main coordinator, button gestures, state machine
│ ├── sound_picture_app.ino # Main coordinator, button click gesture parser, state machine
│ ├── Display_ST7789.h/.cpp # Waveshare display initialization & backlight control
│ ├── Display_PNG.h/.cpp # PNGdec utility for decoding and drawing images
│ ├── SD_Card.h/.cpp # SD_MMC mount and folder retrieval helpers
│ └── Audio_Player.h/.cpp # WAV parser and background I2S player thread
│ └── Audio_Player.h/.cpp # background ESP32-audioI2S player task wrapper
├── ESP32-S3-LCD-1.47-Demo/ # Waveshare Official Reference Demos
└── README.md # This file
```
@@ -61,10 +65,10 @@ Configure the pin connections as follows:
* **Extension:** `.png`
* **Resolution:** Sized to **172 x 320** pixels.
3. Place your sound files in the root directory:
* **Extension:** `.wav`
* **Format:** Uncompressed PCM WAV (Supports Mono/Stereo, 8/16/24/32-bit depths).
* **Extension:** `.mp3` or `.wav`
* **Format:** Compressed MP3 or uncompressed PCM WAV.
4. Ensure file names map alphabetically:
* `photo1.png` <--> `sound1.wav`
* `photo1.png` <--> `sound1.mp3`
* `photo2.png` <--> `sound2.wav`
---
@@ -78,8 +82,9 @@ Make sure you have the official Espressif board package installed:
* Go to **Tools > Board > Boards Manager**, search for **`esp32`** (by Espressif Systems), and install version **3.0.2 or higher**.
### 2. Install Required Libraries
* Go to **Sketch > Include Library > Manage Libraries...**
* Search for **`PNGdec`** (by Larry Bank) and click **Install**.
You need to install the following two libraries in the IDE via **Sketch > Include Library > Manage Libraries...**:
1. **`PNGdec`** (by Larry Bank): Decoding and drawing PNGs.
2. **`ESP32-audioI2S`** (by Wolle / schreibfaul1): Asynchronous MP3/WAV decoding.
### 3. Select Board Configurations
Go to the **Tools** menu and set the following variables:
@@ -93,16 +98,3 @@ Go to the **Tools** menu and set the following variables:
1. Connect your board to your computer via USB.
2. Select the corresponding COM Port under **Tools > Port**.
3. Click **Upload** (or press `Ctrl+U`).
---
## 🧠 Code Architecture & Multi-threading
Drawing to SPI displays and reading from SD cards are heavily blocking SPI transactions. To prevent audio dropouts and stutters while loading images, the firmware divides work across the ESP32-S3's dual cores:
* **Core 1 (Main Thread):** Runs the standard Arduino `loop()`. It performs debounced button polling, updates the NeoPixel LED pulse timers, and controls the display state transitions.
* **Core 0 (Audio Thread):** When a WAV file is selected, `playWav()` spawns a background thread pinned to Core 0. This thread handles:
1. Opening the file handler.
2. Parsing raw WAV headers dynamically (calculating sample rate, bit depth, channel configuration).
3. Re-initializing the standard ESP32 `ESP_I2S` driver configuration.
4. Streaming sample data chunks into I2S DMA buffers, pausing and yielding if the DMA buffer fills.

View File

@@ -35,7 +35,7 @@ void pngDraw(PNGDRAW *pDraw) {
png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_BIG_ENDIAN, 0xffffffff);
uint32_t size = pDraw->iWidth;
for (size_t i = 0; i < size; i++) {
lineBuffer[i] = (((lineBuffer[i] >> 8) & 0xFF) | ((lineBuffer[i] << 8) & 0xFF00));
lineBuffer[i] = __builtin_bswap16(lineBuffer[i]);
}
LCD_addWindow(xpos, pDraw->y, xpos + pDraw->iWidth - 1, ypos + pDraw->y, lineBuffer);
}

View File

@@ -207,9 +207,14 @@ void LCD_addWindow(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yen
uint16_t Show_Width = Xend - Xstart + 1;
uint16_t Show_Height = Yend - Ystart + 1;
uint32_t numBytes = Show_Width * Show_Height * sizeof(uint16_t);
uint8_t Read_D[numBytes];
LCD_SetCursor(Xstart, Ystart, Xend, Yend);
LCD_WriteData_nbyte((uint8_t*)color, Read_D, numBytes);
LCDspi.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0));
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, HIGH);
LCDspi.writeBytes((uint8_t*)color, numBytes);
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH);
LCDspi.endTransaction();
}
// backlight
void Backlight_Init(void)

View File

@@ -11,8 +11,8 @@ void SD_Init() {
printf("SD MMC: Pin change failed!\r\n");
return;
}
if (SD_MMC.begin("/sdcard", true, true)) {
printf("SD card initialization successful!\r\n");
if (SD_MMC.begin("/sdcard", false, true)) {
printf("SD card initialization successful (4-bit mode)!\r\n");
} else {
printf("SD card initialization failed!\r\n");
}