forked from gronod/squeezelite-esp32
added support fo guition board
This commit is contained in:
189
GUITION_SETUP.md
Normal file
189
GUITION_SETUP.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# Guition JC4827W543C Board Setup Guide
|
||||
|
||||
This guide explains how to configure and build squeezelite-esp32 for the Guition JC4827W543C development board.
|
||||
|
||||
## Board Features
|
||||
|
||||
- **Processor**: ESP32-S3-WROOM-1 (dual-core, 240MHz)
|
||||
- **Display**: 4.3" ILI9488 LCD (480x272 pixels)
|
||||
- **Touch**: GT911 capacitive touch controller
|
||||
- **Interface**: QSPI display interface
|
||||
- **Memory**: 4MB Flash, 4MB PSRAM
|
||||
- **Connectivity**: WiFi, Bluetooth
|
||||
|
||||
## Hardware Configuration
|
||||
|
||||
### Display Connections (QSPI)
|
||||
- **CLK**: GPIO47
|
||||
- **DATA0**: GPIO21
|
||||
- **DATA1**: GPIO48
|
||||
- **DATA2**: GPIO40
|
||||
- **DATA3**: GPIO39
|
||||
- **CS**: GPIO45
|
||||
- **DC**: Not used with QSPI
|
||||
- **RST**: GPIO38 (if available)
|
||||
|
||||
### I2C Connections (Touch)
|
||||
- **SDA**: GPIO8
|
||||
- **SCL**: GPIO4
|
||||
- **Touch INT**: GPIO3
|
||||
- **Touch RST**: GPIO38
|
||||
|
||||
### Audio I2S (External DAC Required)
|
||||
- **BCK**: GPIO15
|
||||
- **WS**: GPIO16
|
||||
- **DO**: GPIO17
|
||||
- **DI**: (not used for output only)
|
||||
|
||||
### Other GPIO
|
||||
- **Backlight**: GPIO1 (PWM controlled)
|
||||
|
||||
## Software Configuration
|
||||
|
||||
### 1. Build Configuration
|
||||
|
||||
Use the provided build script:
|
||||
|
||||
```bash
|
||||
# Make the script executable
|
||||
chmod +x build-guition.sh
|
||||
|
||||
# Configure and build
|
||||
./build-guition.sh
|
||||
```
|
||||
|
||||
Or manually:
|
||||
|
||||
```bash
|
||||
# Set ESP32-S3 target
|
||||
export IDF_TARGET=esp32s3
|
||||
|
||||
# Copy Guition configuration
|
||||
cp squeezelite-esp32-Guition-sdkconfig.defaults sdkconfig.defaults
|
||||
|
||||
# Configure project
|
||||
idf.py menuconfig
|
||||
|
||||
# Build
|
||||
idf.py build
|
||||
```
|
||||
|
||||
### 2. Menuconfig Settings
|
||||
|
||||
In `idf.py menuconfig`, select:
|
||||
|
||||
**Target Configuration**:
|
||||
- `Squeezelite-ESP32` → `Guition JC4827W543C`
|
||||
|
||||
**Audio Settings**:
|
||||
- Configure I2S GPIO pins for your external DAC
|
||||
- Default: BCK=15, WS=16, DO=17
|
||||
|
||||
**Display Settings**:
|
||||
- Should be automatically configured as:
|
||||
- Type: QSPI
|
||||
- Driver: ILI9488
|
||||
- Width: 480
|
||||
- Height: 272
|
||||
- CS: 45
|
||||
- Speed: 20MHz
|
||||
|
||||
**I2C Settings**:
|
||||
- SDA: 8
|
||||
- SCL: 4
|
||||
- Speed: 400kHz
|
||||
- Port: 0
|
||||
|
||||
### 3. External Audio DAC
|
||||
|
||||
The Guition board does not have a built-in audio DAC. You must connect an external I2S DAC such as:
|
||||
|
||||
- PCM5102
|
||||
- TAS575x series
|
||||
- ES8388
|
||||
- AC101
|
||||
|
||||
Connect the DAC to the I2S pins and configure it in the NVS settings or through the web UI.
|
||||
|
||||
## Flashing the Firmware
|
||||
|
||||
```bash
|
||||
# Flash to the board
|
||||
idf.py -p /dev/ttyUSB0 flash
|
||||
|
||||
# Monitor output (optional)
|
||||
idf.py -p /dev/ttyUSB0 monitor
|
||||
```
|
||||
|
||||
## First Boot Configuration
|
||||
|
||||
1. Connect to the WiFi AP created by the device (SSID: SqueezeESP32-XXXXXX)
|
||||
2. Open the web configuration interface
|
||||
3. Configure your WiFi network
|
||||
4. Set up your audio DAC if needed
|
||||
5. Configure LMS server connection
|
||||
|
||||
## Display Features
|
||||
|
||||
The ILI9488 driver supports:
|
||||
- 16-bit RGB565 color
|
||||
- Hardware acceleration
|
||||
- Shadow buffering for performance
|
||||
- Automatic dirty region tracking
|
||||
- Rotation and flip options
|
||||
|
||||
## Touch Support
|
||||
|
||||
Touch functionality is planned but not yet implemented. The GT911 touch controller is connected via I2C.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Display Not Working
|
||||
- Check QSPI GPIO connections
|
||||
- Verify CS pin is correctly set to GPIO45
|
||||
- Ensure proper power supply (3.3V for display logic)
|
||||
|
||||
### Audio Not Working
|
||||
- Verify external DAC is properly connected
|
||||
- Check I2S GPIO assignments
|
||||
- Configure DAC model in web UI if using a supported DAC
|
||||
|
||||
### Build Issues
|
||||
- Ensure ESP-IDF supports ESP32-S3
|
||||
- Check that all required components are included
|
||||
- Verify GPIO pin assignments don't conflict
|
||||
|
||||
## Performance Notes
|
||||
|
||||
- The 480x272 display requires significant memory bandwidth
|
||||
- PSRAM is used for display framebuffer (≈260KB)
|
||||
- Audio performance may be affected at high display refresh rates
|
||||
- Consider reducing display update rate if audio issues occur
|
||||
|
||||
## GPIO Map Summary
|
||||
|
||||
| GPIO | Function | Direction |
|
||||
|------|----------|-----------|
|
||||
| 1 | Backlight PWM | Out |
|
||||
| 3 | Touch INT | In |
|
||||
| 4 | I2C SCL | Out |
|
||||
| 8 | I2C SDA | I/O |
|
||||
| 15 | I2S BCK | Out |
|
||||
| 16 | I2S WS | Out |
|
||||
| 17 | I2S DO | Out |
|
||||
| 21 | QSPI DATA0 | Out |
|
||||
| 39 | QSPI DATA3 | Out |
|
||||
| 40 | QSPI DATA2 | Out |
|
||||
| 45 | QSPI CS | Out |
|
||||
| 47 | QSPI CLK | Out |
|
||||
| 48 | QSPI DATA1 | Out |
|
||||
| 38 | Touch RST | Out |
|
||||
|
||||
## Support
|
||||
|
||||
For issues specific to the Guition board implementation:
|
||||
1. Check this README first
|
||||
2. Review the squeezelite-esp32 documentation
|
||||
3. Open an issue on the GitHub repository
|
||||
|
||||
For general squeezelite-esp32 questions, refer to the main project documentation and forums.
|
||||
216
WINDOWS_BUILD_GUIDE.md
Normal file
216
WINDOWS_BUILD_GUIDE.md
Normal file
@@ -0,0 +1,216 @@
|
||||
# Windows Build Guide for Guition JC4827W543C
|
||||
|
||||
This guide explains how to build squeezelite-esp32 for the Guition board on Windows.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **ESP-IDF for Windows**
|
||||
- Download and install ESP-IDF from: https://dl.espressif.com/dl/esp-idf/
|
||||
- Run the installer and follow the setup instructions
|
||||
- Make sure to install the required tools (Git, Python, CMake)
|
||||
|
||||
2. **Visual Studio Build Tools** (if not already installed by ESP-IDF)
|
||||
- Install from Visual Studio Installer
|
||||
- Select "C++ build tools"
|
||||
|
||||
3. **Hardware**
|
||||
- Guition JC4827W543C board
|
||||
- USB cable for power and programming
|
||||
- External I2S audio DAC (required)
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Install ESP-IDF
|
||||
```cmd
|
||||
# Run the ESP-IDF installer
|
||||
# Launch ESP-IDF Command Prompt from Start Menu
|
||||
```
|
||||
|
||||
### 2. Initialize ESP-IDF Environment
|
||||
```cmd
|
||||
# In ESP-IDF Command Prompt
|
||||
cd C:\Espressif\esp-idf
|
||||
export.bat
|
||||
```
|
||||
|
||||
### 3. Clone and Setup Project
|
||||
```cmd
|
||||
# Navigate to your workspace
|
||||
cd C:\Users\YourName\Projects
|
||||
|
||||
# Clone the repository (if not already done)
|
||||
git clone <repository-url> squeezelite-esp32-gronod
|
||||
cd squeezelite-esp32-gronod
|
||||
```
|
||||
|
||||
### 4. Set Environment for Project
|
||||
```cmd
|
||||
# Set ESP-IDF environment for this project
|
||||
C:\Espressif\esp-idf\export.bat
|
||||
```
|
||||
|
||||
## Build Methods
|
||||
|
||||
### Method 1: PowerShell Script (Recommended)
|
||||
```powershell
|
||||
# Open PowerShell as Administrator
|
||||
# Navigate to project directory
|
||||
cd C:\Users\YourName\Projects\squeezelite-esp32-gronod
|
||||
|
||||
# Run the build script
|
||||
.\build-guition.ps1
|
||||
|
||||
# For clean build
|
||||
.\build-guition.ps1 -Clean
|
||||
```
|
||||
|
||||
**Note:** If you get execution policy errors, run:
|
||||
```powershell
|
||||
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
```
|
||||
|
||||
### Method 2: Batch File (Simple)
|
||||
```cmd
|
||||
# Open Command Prompt
|
||||
# Navigate to project directory
|
||||
cd C:\Users\YourName\Projects\squeezelite-esp32-gronod
|
||||
|
||||
# Run the build script
|
||||
build-guition.bat
|
||||
|
||||
# For clean build
|
||||
build-guition.bat clean
|
||||
```
|
||||
|
||||
### Method 3: Manual Commands
|
||||
```cmd
|
||||
# Set ESP-IDF environment
|
||||
C:\Espressif\esp-idf\export.bat
|
||||
|
||||
# Set target
|
||||
set IDF_TARGET=esp32s3
|
||||
|
||||
# Copy Guition config
|
||||
copy /Y squeezelite-esp32-Guition-sdkconfig.defaults sdkconfig.defaults
|
||||
|
||||
# Configure (opens menu interface)
|
||||
idf.py menuconfig
|
||||
|
||||
# Build
|
||||
idf.py build
|
||||
```
|
||||
|
||||
## Menuconfig Configuration
|
||||
|
||||
When `idf.py menuconfig` runs:
|
||||
|
||||
1. **Select Target Hardware**:
|
||||
- Navigate to: `Squeezelite-ESP32` → `Guition JC4827W543C`
|
||||
|
||||
2. **Configure Audio** (if needed):
|
||||
- Navigate to: `Audio settings` → `DAC settings`
|
||||
- Set I2S GPIO pins for your external DAC
|
||||
- Default: BCK=15, WS=16, DO=17
|
||||
|
||||
3. **Save and Exit**:
|
||||
- Press `S` to save
|
||||
- Press `Q` to exit
|
||||
|
||||
## Flashing the Firmware
|
||||
|
||||
### Find Your COM Port
|
||||
```cmd
|
||||
# List all COM ports
|
||||
mode
|
||||
|
||||
# Or check Device Manager under "Ports (COM & LPT)"
|
||||
```
|
||||
|
||||
### Flash and Monitor
|
||||
```cmd
|
||||
# Flash firmware
|
||||
idf.py -p COM3 flash
|
||||
|
||||
# Flash and monitor (recommended)
|
||||
idf.py -p COM3 flash monitor
|
||||
|
||||
# Just monitor (if already flashed)
|
||||
idf.py -p COM3 monitor
|
||||
```
|
||||
|
||||
### Using the Scripts
|
||||
Both PowerShell and batch scripts will ask if you want to flash after building.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **"ESP-IDF environment not found"**
|
||||
- Make sure you're running from ESP-IDF Command Prompt
|
||||
- Or run `C:\Espressif\esp-idf\export.bat` first
|
||||
|
||||
2. **"Python not found"**
|
||||
- ESP-IDF installer should include Python
|
||||
- Check that Python is in your PATH
|
||||
|
||||
3. **"Build tools not found"**
|
||||
- Install Visual Studio Build Tools
|
||||
- Make sure C++ tools are selected
|
||||
|
||||
4. **"Permission denied" (PowerShell)**
|
||||
- Run PowerShell as Administrator
|
||||
- Or set execution policy: `Set-ExecutionPolicy RemoteSigned`
|
||||
|
||||
5. **"COM port not found"**
|
||||
- Check device connections
|
||||
- Install USB drivers if needed (CH340/CP210x)
|
||||
- Check Device Manager
|
||||
|
||||
### Clean Build
|
||||
If you encounter build issues:
|
||||
```cmd
|
||||
# Using batch file
|
||||
build-guition.bat clean
|
||||
|
||||
# Or manually
|
||||
idf.py fullclean
|
||||
idf.py build
|
||||
```
|
||||
|
||||
### Verifying Installation
|
||||
```cmd
|
||||
# Check ESP-IDF version
|
||||
idf.py --version
|
||||
|
||||
# Check target
|
||||
echo %IDF_TARGET%
|
||||
|
||||
# List available targets
|
||||
idf.py --help | findstr target
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
1. **Initial Setup**: Run the build script once to configure
|
||||
2. **Code Changes**: Make your modifications
|
||||
3. **Build**: Run `.\build-guition.ps1` or `build-guition.bat`
|
||||
4. **Flash**: Let the script flash automatically, or use `idf.py -p COMX flash`
|
||||
5. **Monitor**: Use `idf.py -p COMX monitor` to view logs
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- **SSD Storage**: Build on SSD for much faster compilation
|
||||
- **RAM**: 8GB+ recommended for large projects
|
||||
- **CPU**: Multi-core CPU helps with parallel compilation
|
||||
- **Antivirus**: Exclude project directory from real-time scanning
|
||||
|
||||
## Next Steps
|
||||
|
||||
After successful build and flash:
|
||||
|
||||
1. Connect to WiFi AP created by device
|
||||
2. Configure your network settings
|
||||
3. Set up audio DAC configuration
|
||||
4. Connect to your Logitech Media Server
|
||||
|
||||
For more detailed configuration, see `GUITION_SETUP.md`.
|
||||
80
build-guition.bat
Normal file
80
build-guition.bat
Normal file
@@ -0,0 +1,80 @@
|
||||
@echo off
|
||||
REM Build script for Guition JC4827W543C board - Batch file version
|
||||
REM Usage: build-guition.bat [clean]
|
||||
|
||||
echo Building Squeezelite-ESP32 for Guition JC4827W543C
|
||||
|
||||
REM Check if ESP-IDF environment is set up
|
||||
if "%IDF_PATH%"=="" (
|
||||
echo Error: ESP-IDF environment not found!
|
||||
echo Please run esp-idf\export.bat first
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM Set target to ESP32-S3
|
||||
set IDF_TARGET=esp32s3
|
||||
echo Target set to: %IDF_TARGET%
|
||||
|
||||
REM Copy Guition-specific configuration
|
||||
if exist "squeezelite-esp32-Guition-sdkconfig.defaults" (
|
||||
echo Using Guition configuration...
|
||||
copy /Y "squeezelite-esp32-Guition-sdkconfig.defaults" "sdkconfig.defaults"
|
||||
) else (
|
||||
echo Warning: Guition configuration file not found
|
||||
)
|
||||
|
||||
REM Clean if requested
|
||||
if /i "%1"=="clean" (
|
||||
echo Cleaning build...
|
||||
idf.py fullclean
|
||||
if errorlevel 1 (
|
||||
echo Clean failed!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
REM Configure the project
|
||||
echo Configuring project...
|
||||
echo Running: idf.py menuconfig
|
||||
echo Select: Squeezelite-ESP32 -^> Guition JC4827W543C
|
||||
echo.
|
||||
echo Press any key to continue to menuconfig...
|
||||
pause > nul
|
||||
|
||||
idf.py menuconfig
|
||||
if errorlevel 1 (
|
||||
echo Configuration failed!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM Build the project
|
||||
echo Building firmware...
|
||||
idf.py build
|
||||
if errorlevel 1 (
|
||||
echo Build failed!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo Build complete!
|
||||
echo Firmware location: build\squeezelite.bin
|
||||
echo.
|
||||
echo To flash the firmware:
|
||||
echo idf.py -p ^<PORT^> flash
|
||||
echo.
|
||||
echo To monitor output:
|
||||
echo idf.py -p ^<PORT^> monitor
|
||||
|
||||
REM Ask if user wants to flash
|
||||
echo.
|
||||
set /p flash="Do you want to flash the firmware now? (y/n) "
|
||||
if /i "%flash%"=="y" (
|
||||
set /p port="Enter COM port (e.g., COM3): "
|
||||
echo Flashing to %port%...
|
||||
idf.py -p %port% flash monitor
|
||||
)
|
||||
|
||||
pause
|
||||
76
build-guition.ps1
Normal file
76
build-guition.ps1
Normal file
@@ -0,0 +1,76 @@
|
||||
# Build script for Guition JC4827W543C board - PowerShell version
|
||||
# Usage: .\build-guition.ps1 [-Clean]
|
||||
|
||||
param(
|
||||
[switch]$Clean
|
||||
)
|
||||
|
||||
Write-Host "Building Squeezelite-ESP32 for Guition JC4827W543C" -ForegroundColor Green
|
||||
|
||||
# Check if ESP-IDF environment is set up
|
||||
if (-not $env:IDF_PATH) {
|
||||
Write-Host "Error: ESP-IDF environment not found!" -ForegroundColor Red
|
||||
Write-Host "Please run esp-idf/export.bat first" -ForegroundColor Yellow
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Set target to ESP32-S3
|
||||
$env:IDF_TARGET = "esp32s3"
|
||||
Write-Host "Target set to: $env:IDF_TARGET" -ForegroundColor Cyan
|
||||
|
||||
# Copy Guition-specific configuration
|
||||
if (Test-Path "squeezelite-esp32-Guition-sdkconfig.defaults") {
|
||||
Write-Host "Using Guition configuration..." -ForegroundColor Cyan
|
||||
Copy-Item "squeezelite-esp32-Guition-sdkconfig.defaults" "sdkconfig.defaults" -Force
|
||||
} else {
|
||||
Write-Host "Warning: Guition configuration file not found" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Clean if requested
|
||||
if ($Clean) {
|
||||
Write-Host "Cleaning build..." -ForegroundColor Cyan
|
||||
idf.py fullclean
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Clean failed!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Configure the project
|
||||
Write-Host "Configuring project..." -ForegroundColor Cyan
|
||||
Write-Host "Running: idf.py menuconfig" -ForegroundColor White
|
||||
Write-Host "Select: Squeezelite-ESP32 -> Guition JC4827W543C" -ForegroundColor Yellow
|
||||
Write-Host "Press Enter to continue to menuconfig..." -ForegroundColor White
|
||||
Read-Host
|
||||
|
||||
idf.py menuconfig
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Configuration failed!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Build the project
|
||||
Write-Host "Building firmware..." -ForegroundColor Cyan
|
||||
idf.py build
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Build failed!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Build complete!" -ForegroundColor Green
|
||||
Write-Host "Firmware location: build\squeezelite.bin" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "To flash the firmware:" -ForegroundColor White
|
||||
Write-Host "idf.py -p <PORT> flash" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host "To monitor output:" -ForegroundColor White
|
||||
Write-Host "idf.py -p <PORT> monitor" -ForegroundColor Yellow
|
||||
|
||||
# Ask if user wants to flash
|
||||
Write-Host ""
|
||||
$flash = Read-Host "Do you want to flash the firmware now? (y/n)"
|
||||
if ($flash -eq 'y' -or $flash -eq 'Y') {
|
||||
$port = Read-Host "Enter COM port (e.g., COM3)"
|
||||
Write-Host "Flashing to $port..." -ForegroundColor Cyan
|
||||
idf.py -p $port flash monitor
|
||||
}
|
||||
40
build-guition.sh
Normal file
40
build-guition.sh
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Build script for Guition JC4827W543C board
|
||||
# Usage: ./build-guition.sh [clean]
|
||||
|
||||
set -e
|
||||
|
||||
echo "Building Squeezelite-ESP32 for Guition JC4827W543C"
|
||||
|
||||
# Set target to ESP32-S3
|
||||
export IDF_TARGET=esp32s3
|
||||
|
||||
# Copy Guition-specific configuration
|
||||
if [ -f "squeezelite-esp32-Guition-sdkconfig.defaults" ]; then
|
||||
echo "Using Guition configuration..."
|
||||
cp squeezelite-esp32-Guition-sdkconfig.defaults sdkconfig.defaults
|
||||
fi
|
||||
|
||||
# Clean if requested
|
||||
if [ "$1" == "clean" ]; then
|
||||
echo "Cleaning build..."
|
||||
idf.py fullclean
|
||||
fi
|
||||
|
||||
# Configure with Guition target
|
||||
echo "Configuring project..."
|
||||
idf.py menuconfig
|
||||
|
||||
# Build the project
|
||||
echo "Building firmware..."
|
||||
idf.py build
|
||||
|
||||
echo "Build complete!"
|
||||
echo "Firmware location: build/squeezelite.bin"
|
||||
echo ""
|
||||
echo "To flash the firmware:"
|
||||
echo "idf.py -p <PORT> flash"
|
||||
echo ""
|
||||
echo "To monitor output:"
|
||||
echo "idf.py -p <PORT> monitor"
|
||||
297
components/display/ILI9488.c
Normal file
297
components/display/ILI9488.c
Normal file
@@ -0,0 +1,297 @@
|
||||
/**
|
||||
* ILI9488 Display Driver for Guition JC4827W543C
|
||||
* Supports QSPI interface for 480x272 resolution
|
||||
* Based on ILI9341 driver adapted for ILI9488
|
||||
*
|
||||
* (c) Guition Support 2026
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "gds.h"
|
||||
#include "gds_private.h"
|
||||
|
||||
#define SHADOW_BUFFER
|
||||
#define USE_IRAM
|
||||
#define PAGE_BLOCK 4096
|
||||
#define ENABLE_WRITE 0x2c
|
||||
|
||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
static char TAG[] = "ILI9488";
|
||||
|
||||
struct PrivateSpace {
|
||||
uint8_t *iRAM, *Shadowbuffer;
|
||||
struct {
|
||||
uint16_t Height, Width;
|
||||
} Offset;
|
||||
uint8_t MADCtl, PageSize;
|
||||
uint8_t Model;
|
||||
};
|
||||
|
||||
// ILI9488 Commands
|
||||
static const uint8_t ILI9488_INIT_SEQUENCE[] = {
|
||||
// Software reset
|
||||
0x01, 0x80, 150, // SWRESET, delay 150ms
|
||||
// Power control
|
||||
0xD0, 3, 0x07, 0x42, 0x18, // Power Control
|
||||
0xD1, 3, 0x00, 0x07, 0x10, // VCOM Control
|
||||
0xD2, 1, 0x01, // Power Control for Normal Mode
|
||||
0xC0, 2, 0x10, 0x3B, // Panel Driving Setting
|
||||
0xC1, 1, 0x10, // Frame Rate Control
|
||||
0xC5, 5, 0x0A, 0x3A, 0x28, 0x28, 0x02, // MCU Control
|
||||
0xC6, 1, 0x00, // Frame Rate Control
|
||||
0xB1, 2, 0x00, 0x1B, // Display Function Control
|
||||
0xB4, 1, 0x02, // Inversion Control
|
||||
0xB6, 3, 0x02, 0x02, 0x3B, // Display Function Control
|
||||
0xB7, 1, 0xC6, // Entry Mode Set
|
||||
0xE0, 16, 0x00, 0x07, 0x10, 0x0E, 0x09, 0x16, 0x06, 0x0A,
|
||||
0x0E, 0x09, 0x15, 0x0D, 0x0E, 0x11, 0x0F, 0x12, // Positive Gamma Control
|
||||
0xE1, 16, 0x00, 0x17, 0x1A, 0x04, 0x0E, 0x06, 0x2F, 0x24,
|
||||
0x1B, 0x1B, 0x22, 0x1F, 0x1E, 0x37, 0x3F, 0x00, // Negative Gamma Control
|
||||
0x36, 1, 0xE8, // Memory Access Control (MX, MY, RGB mode)
|
||||
0x3A, 1, 0x55, // Interface Pixel Format (16bpp)
|
||||
0x11, 0x80, 150, // Sleep Out, delay 150ms
|
||||
0x29, 0x80, 50, // Display On, delay 50ms
|
||||
0xFF, 0x00 // End of sequence
|
||||
};
|
||||
|
||||
static void WriteByte( struct GDS_Device* Device, uint8_t Data ) {
|
||||
Device->WriteData( Device, &Data, 1 );
|
||||
}
|
||||
|
||||
static void SetColumnAddress( struct GDS_Device* Device, uint16_t Start, uint16_t End ) {
|
||||
uint32_t Addr = __builtin_bswap16(Start) | (__builtin_bswap16(End) << 16);
|
||||
Device->WriteCommand( Device, 0x2A );
|
||||
Device->WriteData( Device, (uint8_t*) &Addr, 4 );
|
||||
}
|
||||
|
||||
static void SetRowAddress( struct GDS_Device* Device, uint16_t Start, uint16_t End ) {
|
||||
uint32_t Addr = __builtin_bswap16(Start) | (__builtin_bswap16(End) << 16);
|
||||
Device->WriteCommand( Device, 0x2B );
|
||||
Device->WriteData( Device, (uint8_t*) &Addr, 4 );
|
||||
}
|
||||
|
||||
static void Update16( struct GDS_Device* Device ) {
|
||||
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
|
||||
|
||||
#ifdef SHADOW_BUFFER
|
||||
uint32_t *optr = (uint32_t*) Private->Shadowbuffer, *iptr = (uint32_t*) Device->Framebuffer;
|
||||
int FirstCol = Device->Width / 2, LastCol = 0, FirstRow = -1, LastRow = 0;
|
||||
|
||||
for (int r = 0; r < Device->Height; r++) {
|
||||
// look for change and update shadow (cheap optimization = width is always a multiple of 2)
|
||||
for (int c = 0; c < Device->Width / 2; c++, iptr++, optr++) {
|
||||
if (*optr != *iptr) {
|
||||
*optr = *iptr;
|
||||
if (c < FirstCol) FirstCol = c;
|
||||
if (c > LastCol) LastCol = c;
|
||||
if (FirstRow < 0) FirstRow = r;
|
||||
LastRow = r;
|
||||
}
|
||||
}
|
||||
|
||||
// wait for a large enough window - careful that window size might increase by more than a line at once !
|
||||
if (FirstRow < 0 || ((LastCol - FirstCol + 1) * (r - FirstRow + 1) * 4 < PAGE_BLOCK && r != Device->Height - 1)) continue;
|
||||
|
||||
FirstCol *= 2;
|
||||
LastCol = LastCol * 2 + 1;
|
||||
SetRowAddress( Device, FirstRow + Private->Offset.Height, LastRow + Private->Offset.Height);
|
||||
SetColumnAddress( Device, FirstCol + Private->Offset.Width, LastCol + Private->Offset.Width );
|
||||
Device->WriteCommand( Device, ENABLE_WRITE );
|
||||
|
||||
int ChunkSize = (LastCol - FirstCol + 1) * 2;
|
||||
|
||||
// own use of IRAM has not proven to be much better than letting SPI do its copy
|
||||
if (Private->iRAM) {
|
||||
uint8_t *optr = Private->iRAM;
|
||||
for (int i = FirstRow; i <= LastRow; i++) {
|
||||
memcpy(optr, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize);
|
||||
optr += ChunkSize;
|
||||
if (optr - Private->iRAM <= (PAGE_BLOCK - ChunkSize) && i < LastRow) continue;
|
||||
Device->WriteData(Device, Private->iRAM, optr - Private->iRAM);
|
||||
optr = Private->iRAM;
|
||||
}
|
||||
} else for (int i = FirstRow; i <= LastRow; i++) {
|
||||
Device->WriteData( Device, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize );
|
||||
}
|
||||
|
||||
FirstCol = Device->Width / 2;
|
||||
LastCol = 0;
|
||||
FirstRow = -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Clear( struct GDS_Device* Device ) {
|
||||
memset( Device->Framebuffer, 0, Device->Width * Device->Height * 2 );
|
||||
Device->Update( Device);
|
||||
}
|
||||
|
||||
static void SetPixel( struct GDS_Device* Device, int X, int Y, uint32_t Color ) {
|
||||
if( X < 0 || X >= Device->Width || Y < 0 || Y >= Device->Height) return;
|
||||
|
||||
*((uint16_t*) Device->Framebuffer + (Y * Device->Width) + X) = (uint16_t) Color;
|
||||
}
|
||||
|
||||
static uint32_t GetPixel( struct GDS_Device* Device, int X, int Y ) {
|
||||
if( X < 0 || X >= Device->Width || Y < 0 || Y >= Device->Height) return 0;
|
||||
|
||||
return *((uint16_t*) Device->Framebuffer + (Y * Device->Width) + X);
|
||||
}
|
||||
|
||||
static void DrawPixel( struct GDS_Device* Device, int X, int Y, uint32_t Color ) {
|
||||
SetPixel( Device, X, Y, Color );
|
||||
}
|
||||
|
||||
static void DrawPixelFast( struct GDS_Device* Device, int X, int Y, uint32_t Color ) {
|
||||
*((uint16_t*) Device->Framebuffer + (Y * Device->Width) + X) = (uint16_t) Color;
|
||||
}
|
||||
|
||||
static void DrawCBR( struct GDS_Device* Device, int X, int Y, int Width, int Height, uint32_t Color ) {
|
||||
uint16_t *fb = (uint16_t*) Device->Framebuffer + Y * Device->Width + X;
|
||||
|
||||
for( int y = 0; y < Height; y++) {
|
||||
for( int x = 0; x < Width; x++) {
|
||||
fb[x] = (uint16_t) Color;
|
||||
}
|
||||
fb += Device->Width;
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawHLine( struct GDS_Device* Device, int X0, int X1, int Y, uint32_t Color ) {
|
||||
if( Y < 0 || Y >= Device->Height) return;
|
||||
|
||||
if( X0 > X1) {
|
||||
int Temp = X0;
|
||||
X0 = X1;
|
||||
X1 = Temp;
|
||||
}
|
||||
|
||||
if( X0 < 0) X0 = 0;
|
||||
if( X1 >= Device->Width) X1 = Device->Width - 1;
|
||||
|
||||
uint16_t *fb = (uint16_t*) Device->Framebuffer + Y * Device->Width + X0;
|
||||
|
||||
for( int x = X0; x <= X1; x++) {
|
||||
*fb++ = (uint16_t) Color;
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawVLine( struct GDS_Device* Device, int X, int Y0, int Y1, uint32_t Color ) {
|
||||
if( X < 0 || X >= Device->Width) return;
|
||||
|
||||
if( Y0 > Y1) {
|
||||
int Temp = Y0;
|
||||
Y0 = Y1;
|
||||
Y1 = Temp;
|
||||
}
|
||||
|
||||
if( Y0 < 0) Y0 = 0;
|
||||
if( Y1 >= Device->Height) Y1 = Device->Height - 1;
|
||||
|
||||
uint16_t *fb = (uint16_t*) Device->Framebuffer + Y0 * Device->Width + X;
|
||||
|
||||
for( int y = Y0; y <= Y1; y++) {
|
||||
*fb = (uint16_t) Color;
|
||||
fb += Device->Width;
|
||||
}
|
||||
}
|
||||
|
||||
static bool Init( struct GDS_Device* Device ) {
|
||||
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
|
||||
const uint8_t *p = ILI9488_INIT_SEQUENCE;
|
||||
|
||||
ESP_LOGI(TAG, "Initializing ILI9488 display %dx%d", Device->Width, Device->Height);
|
||||
|
||||
// Allocate IRAM buffer if available
|
||||
Private->iRAM = (uint8_t*) heap_caps_malloc(PAGE_BLOCK, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
||||
if (!Private->iRAM) {
|
||||
ESP_LOGW(TAG, "Could not allocate IRAM buffer, using direct write");
|
||||
}
|
||||
|
||||
#ifdef SHADOW_BUFFER
|
||||
Private->Shadowbuffer = (uint8_t*) heap_caps_malloc(Device->Width * Device->Height * 2, MALLOC_CAP_DMA);
|
||||
if (!Private->Shadowbuffer) {
|
||||
ESP_LOGE(TAG, "Could not allocate shadow buffer");
|
||||
if (Private->iRAM) free(Private->iRAM);
|
||||
return false;
|
||||
}
|
||||
memset(Private->Shadowbuffer, 0, Device->Width * Device->Height * 2);
|
||||
#endif
|
||||
|
||||
// Send initialization sequence
|
||||
while(*p != 0xFF) {
|
||||
uint8_t cmd = *p++;
|
||||
uint8_t len = (*p & 0x7F);
|
||||
bool delay = (*p++ & 0x80);
|
||||
|
||||
Device->WriteCommand(Device, cmd);
|
||||
if(len) Device->WriteData(Device, (uint8_t*)p, len);
|
||||
p += len;
|
||||
|
||||
if(delay) {
|
||||
uint8_t ms = *p++;
|
||||
vTaskDelay(ms / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
// Set orientation and layout
|
||||
Private->MADCtl = 0xE8; // MX=1, MY=1, RGB=1, BGR=0
|
||||
Device->WriteCommand(Device, 0x36);
|
||||
Device->WriteData(Device, &Private->MADCtl, 1);
|
||||
|
||||
// Set pixel format to 16-bit
|
||||
uint8_t fmt = 0x55;
|
||||
Device->WriteCommand(Device, 0x3A);
|
||||
Device->WriteData(Device, &fmt, 1);
|
||||
|
||||
// Turn on display
|
||||
Device->WriteCommand(Device, 0x29);
|
||||
|
||||
ESP_LOGI(TAG, "ILI9488 initialization complete");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Deinit( struct GDS_Device* Device ) {
|
||||
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
|
||||
|
||||
if (Private->iRAM) free(Private->iRAM);
|
||||
#ifdef SHADOW_BUFFER
|
||||
if (Private->Shadowbuffer) free(Private->Shadowbuffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct GDS_Device* ILI9488_Detect( char *Driver, struct GDS_Device *Device ) {
|
||||
if(strcasecmp(Driver, "ILI9488") != 0) return NULL;
|
||||
|
||||
Device->Private = calloc(1, sizeof(struct PrivateSpace));
|
||||
if(!Device->Private) {
|
||||
ESP_LOGE(TAG, "Cannot allocate private data");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Device->Mode = GDS_RGB565;
|
||||
Device->Depth = 16;
|
||||
Device->Update = Update16;
|
||||
Device->Clear = Clear;
|
||||
Device->SetPixel = SetPixel;
|
||||
Device->GetPixel = GetPixel;
|
||||
Device->DrawPixel = DrawPixel;
|
||||
Device->DrawPixelFast = DrawPixelFast;
|
||||
Device->DrawCBR = DrawCBR;
|
||||
Device->DrawHLine = DrawHLine;
|
||||
Device->DrawVLine = DrawVLine;
|
||||
Device->Init = Init;
|
||||
Device->Deinit = Deinit;
|
||||
|
||||
ESP_LOGI(TAG, "ILI9488 driver loaded");
|
||||
return Device;
|
||||
}
|
||||
@@ -13,6 +13,10 @@ bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int
|
||||
bool GDS_SPIInit( int SPI, int DC );
|
||||
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed, int BacklightPin, int Mode );
|
||||
|
||||
bool GDS_QSPIInit( int QSPI, int DC );
|
||||
bool GDS_QSPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed, int BacklightPin, int Mode );
|
||||
bool GDS_QSPIBusInit( int MOSIPin, int MISOPin, int CLKPin, int Host );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
161
components/display/core/ifaces/default_if_qspi.c
Normal file
161
components/display/core/ifaces/default_if_qspi.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* QSPI Interface for Guition JC4827W543C ILI9488 Display
|
||||
* Supports ESP32-S3 QSPI peripheral
|
||||
*
|
||||
* (c) Guition Support 2026
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <driver/spi_master.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <freertos/task.h>
|
||||
#include "gds.h"
|
||||
#include "gds_err.h"
|
||||
#include "gds_private.h"
|
||||
#include "gds_default_if.h"
|
||||
|
||||
static const int GDS_QSPI_Command_Mode = 0;
|
||||
static const int GDS_QSPI_Data_Mode = 1;
|
||||
|
||||
static spi_host_device_t QSPIHost;
|
||||
static int DCPin;
|
||||
|
||||
static bool QSPIDefaultWriteBytes( spi_device_handle_t QSPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength );
|
||||
static bool QSPIDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command );
|
||||
static bool QSPIDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength );
|
||||
|
||||
bool GDS_QSPIInit( int QSPI, int DC ) {
|
||||
QSPIHost = QSPI;
|
||||
DCPin = DC;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GDS_QSPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int BackLightPin, int Speed, int Mode ) {
|
||||
spi_device_interface_config_t QSPIDeviceConfig = { };
|
||||
spi_device_handle_t QSPIDevice;
|
||||
|
||||
NullCheck( Device, return false );
|
||||
|
||||
if (CSPin >= 0) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( CSPin, GPIO_MODE_OUTPUT ), return false );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( CSPin, 0 ), return false );
|
||||
}
|
||||
|
||||
QSPIDeviceConfig.clock_speed_hz = Speed > 0 ? Speed : SPI_MASTER_FREQ_20M;
|
||||
QSPIDeviceConfig.spics_io_num = CSPin;
|
||||
QSPIDeviceConfig.queue_size = 4;
|
||||
QSPIDeviceConfig.mode = Mode;
|
||||
QSPIDeviceConfig.flags = SPI_DEVICE_HALFDUPLEX;
|
||||
QSPIDeviceConfig.duty_cycle_pos = 128; // 50% duty cycle
|
||||
|
||||
// QSPI specific configuration for ESP32-S3
|
||||
QSPIDeviceConfig.command_bits = 8;
|
||||
QSPIDeviceConfig.address_bits = 24;
|
||||
QSPIDeviceConfig.address_len = 3;
|
||||
|
||||
if( spi_bus_add_device( QSPIHost, &QSPIDeviceConfig, &QSPIDevice ) != ESP_OK ) {
|
||||
GDS_LOG_ERROR( "Failed to add QSPI device to host" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( DCPin >= 0 ) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( DCPin, GPIO_MODE_OUTPUT ), return false );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( DCPin, 1 ), return false );
|
||||
}
|
||||
|
||||
if( RSTPin >= 0 ) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( RSTPin, GPIO_MODE_OUTPUT ), return false );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( RSTPin, 1 ), return false );
|
||||
}
|
||||
|
||||
if( BackLightPin >= 0 ) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( BackLightPin, GPIO_MODE_OUTPUT ), return false );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( BackLightPin, 1 ), return false );
|
||||
}
|
||||
|
||||
Device->WriteCommand = QSPIDefaultWriteCommand;
|
||||
Device->WriteData = QSPIDefaultWriteData;
|
||||
Device->DeviceHandle = QSPIDevice;
|
||||
|
||||
// Hardware reset if pin is available
|
||||
if( RSTPin >= 0 ) {
|
||||
gpio_set_level( RSTPin, 0 );
|
||||
vTaskDelay( pdMS_TO_TICKS( 10 ) );
|
||||
gpio_set_level( RSTPin, 1 );
|
||||
vTaskDelay( pdMS_TO_TICKS( 120 ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool QSPIDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command ) {
|
||||
return QSPIDefaultWriteBytes( Device->DeviceHandle, GDS_QSPI_Command_Mode, &Command, 1 );
|
||||
}
|
||||
|
||||
static bool QSPIDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength ) {
|
||||
if( DCPin >= 0 ) {
|
||||
gpio_set_level( DCPin, 1 );
|
||||
}
|
||||
|
||||
bool Result = QSPIDefaultWriteBytes( Device->DeviceHandle, GDS_QSPI_Data_Mode, Data, DataLength );
|
||||
|
||||
if( DCPin >= 0 ) {
|
||||
gpio_set_level( DCPin, 0 );
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static bool QSPIDefaultWriteBytes( spi_device_handle_t QSPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength ) {
|
||||
spi_transaction_ext_t SPITransaction = { };
|
||||
SPITransaction.base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_DUMMY;
|
||||
SPITransaction.base.cmd = 0;
|
||||
SPITransaction.base.addr = 0;
|
||||
SPITransaction.base.length = DataLength * 8;
|
||||
SPITransaction.base.tx_buffer = Data;
|
||||
SPITransaction.base.rx_buffer = NULL;
|
||||
SPITransaction.command_bits = 8;
|
||||
SPITransaction.address_bits = 0;
|
||||
SPITransaction.dummy_bits = 0;
|
||||
|
||||
if( WriteMode == GDS_QSPI_Command_Mode ) {
|
||||
SPITransaction.command_bits = 8;
|
||||
SPITransaction.base.cmd = Data[0];
|
||||
SPITransaction.base.length = 0;
|
||||
SPITransaction.base.tx_buffer = NULL;
|
||||
}
|
||||
|
||||
if( spi_device_transmit( QSPIHandle, (spi_transaction_t*) &SPITransaction ) != ESP_OK ) {
|
||||
GDS_LOG_ERROR( "QSPI transaction failed" );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GDS_QSPIBusInit( int MOSIPin, int MISOPin, int CLKPin, int Host ) {
|
||||
spi_bus_config_t BusConfig = { };
|
||||
|
||||
NullCheck( Host >= SPI2_HOST && Host <= SPI3_HOST, return false );
|
||||
|
||||
// QSPI pin configuration for ESP32-S3
|
||||
BusConfig.mosi_io_num = MOSIPin;
|
||||
BusConfig.miso_io_num = MISOPin;
|
||||
BusConfig.sclk_io_num = CLKPin;
|
||||
BusConfig.quadwp_io_num = -1; // Not used for ILI9488
|
||||
BusConfig.quadhd_io_num = -1; // Not used for ILI9488
|
||||
BusConfig.max_transfer_sz = 65536;
|
||||
BusConfig.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_NATIVE_PINS;
|
||||
|
||||
if( spi_bus_initialize( Host, &BusConfig, DMA_CH_AUTO ) != ESP_OK ) {
|
||||
GDS_LOG_ERROR( "Failed to initialize QSPI bus" );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -73,6 +73,7 @@ static const char *known_drivers[] = {"SH1106",
|
||||
"ST7735",
|
||||
"ST7789",
|
||||
"ILI9341",
|
||||
"ILI9488",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -80,8 +81,8 @@ static void displayer_task(void *args);
|
||||
static void display_sleep(void);
|
||||
|
||||
struct GDS_Device *display;
|
||||
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SH1122_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect;
|
||||
GDS_DetectFunc *drivers[] = { SH1106_Detect, SH1122_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect, NULL };
|
||||
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SH1122_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect, ILI9488_Detect;
|
||||
GDS_DetectFunc *drivers[] = { SH1106_Detect, SH1122_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect, ILI9488_Detect, NULL };
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
@@ -134,6 +135,31 @@ void display_init(char *welcome) {
|
||||
GDS_SPIAttachDevice( display, width, height, CS_pin, RST_pin, backlight_pin, speed, mode );
|
||||
|
||||
ESP_LOGI(TAG, "Display is SPI host %u with cs:%d", spi_system_host, CS_pin);
|
||||
} else if (strcasestr(config, "QSPI") && spi_system_host != -1) {
|
||||
int CS_pin = -1, speed = 0, mode = 0;
|
||||
|
||||
PARSE_PARAM(config, "cs", '=', CS_pin);
|
||||
PARSE_PARAM(config, "speed", '=', speed);
|
||||
PARSE_PARAM(config, "mode", '=', mode);
|
||||
|
||||
// Parse QSPI data pins
|
||||
int data_pins[4] = {-1, -1, -1, -1};
|
||||
char *data_str = strstr(config, "data=");
|
||||
if (data_str) {
|
||||
sscanf(data_str + 5, "%d,%d,%d,%d", &data_pins[0], &data_pins[1], &data_pins[2], &data_pins[3]);
|
||||
}
|
||||
|
||||
init = true;
|
||||
|
||||
// Initialize QSPI bus with data pins
|
||||
if (data_pins[0] != -1 && data_pins[1] != -1 && data_pins[2] != -1 && data_pins[3] != -1) {
|
||||
GDS_QSPIBusInit( data_pins[0], data_pins[1], spi_system_clk_gpio, spi_system_host );
|
||||
}
|
||||
|
||||
GDS_QSPIInit( spi_system_host, spi_system_dc_gpio );
|
||||
GDS_QSPIAttachDevice( display, width, height, CS_pin, RST_pin, backlight_pin, speed, mode );
|
||||
|
||||
ESP_LOGI(TAG, "Display is QSPI host %u with cs:%d", spi_system_host, CS_pin);
|
||||
} else {
|
||||
display = NULL;
|
||||
ESP_LOGI(TAG, "Unsupported display interface or serial link not configured");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
idf_component_register( SRC_DIRS . muse
|
||||
idf_component_register( SRC_DIRS . muse guition
|
||||
INCLUDE_DIRS .
|
||||
PRIV_REQUIRES services
|
||||
)
|
||||
|
||||
5
components/targets/guition/CMakeLists.txt
Normal file
5
components/targets/guition/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "guition.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES driver esp_common
|
||||
)
|
||||
43
components/targets/guition/guition.c
Normal file
43
components/targets/guition/guition.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Guition JC4827W543C board support for squeezelite-esp32
|
||||
*
|
||||
* (c) Guition Board Support 2026
|
||||
* Based on squeezelite-esp32 architecture
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_types.h>
|
||||
#include <esp_system.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include "globdefs.h"
|
||||
#include "monitor.h"
|
||||
#include "targets.h"
|
||||
|
||||
static const char TAG[] = "guition";
|
||||
|
||||
static bool init(void);
|
||||
|
||||
const struct target_s target_guition = {
|
||||
.model = "guition",
|
||||
.init = init
|
||||
};
|
||||
|
||||
static bool init(void) {
|
||||
ESP_LOGI(TAG, "Initializing Guition JC4827W543C board");
|
||||
ESP_LOGI(TAG, "Board features:");
|
||||
ESP_LOGI(TAG, " - ESP32-S3-WROOM-1 processor");
|
||||
ESP_LOGI(TAG, " - 4.3\" ILI9488 display (480x272)");
|
||||
ESP_LOGI(TAG, " - GT911 capacitive touch");
|
||||
ESP_LOGI(TAG, " - QSPI display interface");
|
||||
ESP_LOGI(TAG, " - I2C touch interface (GPIO8/4)");
|
||||
|
||||
// No board-specific hardware initialization required
|
||||
// All configuration is handled through NVS parameters
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "string.h"
|
||||
#include "targets.h"
|
||||
|
||||
const struct target_s *target_set[] = { &target_muse, NULL };
|
||||
const struct target_s *target_set[] = { &target_muse, &target_guition, NULL };
|
||||
|
||||
void target_init(char *target) {
|
||||
for (int i = 0; *target && target_set[i]; i++) if (strcasestr(target_set[i]->model, target)) {
|
||||
|
||||
@@ -20,3 +20,4 @@ struct target_s {
|
||||
};
|
||||
|
||||
extern const struct target_s target_muse;
|
||||
extern const struct target_s target_guition;
|
||||
|
||||
@@ -73,6 +73,10 @@ menu "Squeezelite-ESP32"
|
||||
bool "T-WATCH2020 by LilyGo"
|
||||
select I2C_LOCKED
|
||||
select TARGET_LOCKED
|
||||
config GUITION
|
||||
bool "Guition JC4827W543C"
|
||||
select I2C_LOCKED
|
||||
select TARGET_LOCKED
|
||||
endchoice
|
||||
config RELEASE_API
|
||||
string "Software update URL"
|
||||
@@ -91,12 +95,14 @@ menu "Squeezelite-ESP32"
|
||||
default "SqueezeAMP" if SQUEEZEAMP
|
||||
default "Squeezelite-TWATCH" if TWATCH2020
|
||||
default "Muse" if MUSE
|
||||
default "Squeezelite-Guition" if GUITION
|
||||
default "Squeezelite-ESP32"
|
||||
config FW_PLATFORM_NAME
|
||||
string
|
||||
default "SqueezeAmp" if SQUEEZEAMP
|
||||
default "TWATCH" if TWATCH2020
|
||||
default "Muse" if MUSE
|
||||
default "Guition" if GUITION
|
||||
default "ESP32"
|
||||
# AGGREGATES - begin
|
||||
# these parameters are "aggregates" that take precedence. They must have a default value
|
||||
@@ -105,6 +111,7 @@ menu "Squeezelite-ESP32"
|
||||
default "model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0" if SQUEEZEAMP
|
||||
default "model=I2S,bck=26,ws=25,do=33,i2c=53,sda=21,scl=22" if TWATCH2020
|
||||
default "model=I2S,bck=5,ws=25,do=26,di=35,i2c=16,sda=18,scl=23,mck" if MUSE
|
||||
default "model=I2S,bck=15,ws=16,do=17" if GUITION
|
||||
default ""
|
||||
config SPDIF_CONFIG
|
||||
string
|
||||
@@ -117,10 +124,12 @@ menu "Squeezelite-ESP32"
|
||||
string
|
||||
default "dc=27,data=19,clk=18" if TWATCH2020
|
||||
default "mosi=15,miso=2,clk=14" if MUSE
|
||||
default "clk=47,data=21,48,40,39,cs=45,host=2" if GUITION
|
||||
default ""
|
||||
config DISPLAY_CONFIG
|
||||
string
|
||||
default "SPI,driver=ST7789,width=240,height=240,cs=5,back=12,speed=16000000,HFlip,VFlip" if TWATCH2020
|
||||
default "QSPI,driver=ILI9488,width=480,height=272,cs=45,speed=20000000,invert,rotate" if GUITION
|
||||
default ""
|
||||
config ETH_CONFIG
|
||||
string
|
||||
@@ -143,6 +152,7 @@ menu "Squeezelite-ESP32"
|
||||
config TARGET
|
||||
string
|
||||
default "muse" if MUSE
|
||||
default "guition" if GUITION
|
||||
default ""
|
||||
config AMP_GPIO
|
||||
int
|
||||
@@ -176,6 +186,7 @@ menu "Squeezelite-ESP32"
|
||||
string
|
||||
default "0=ir" if SQUEEZEAMP
|
||||
default "" if TWATCH2020
|
||||
default "1=backlight" if GUITION
|
||||
endmenu
|
||||
|
||||
menu "Audio settings"
|
||||
@@ -344,6 +355,7 @@ menu "Squeezelite-ESP32"
|
||||
visible if !TWATCH2020
|
||||
config I2C_CONFIG
|
||||
string "I2C system configuration"
|
||||
default "sda=8,scl=4,speed=400000,port=0" if GUITION
|
||||
default ""
|
||||
help
|
||||
Set parameters of shared I2C interface
|
||||
|
||||
139
squeezelite-esp32-Guition-sdkconfig.defaults
Normal file
139
squeezelite-esp32-Guition-sdkconfig.defaults
Normal file
@@ -0,0 +1,139 @@
|
||||
#
|
||||
# Guition JC4827W543C ESP32-S3 configuration
|
||||
# Based on I2S-S3-sdkconfig with Guition-specific settings
|
||||
#
|
||||
|
||||
# ESP32-S3 Target Configuration
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
CONFIG_IDF_TARGET_ESP32S3=y
|
||||
CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009
|
||||
|
||||
# ESP32-S3 specific settings
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
|
||||
CONFIG_SPIRAM_SPEED_80M=y
|
||||
CONFIG_SPIRAM_MODE_OCT=y
|
||||
CONFIG_SPIRAM_TYPE_AUTO=y
|
||||
CONFIG_SPIRAM_SIZE_AUTO=y
|
||||
CONFIG_SPIRAM_USE_MALLOC=y
|
||||
CONFIG_SPIRAM_USE_CAPS_ALLOC=y
|
||||
CONFIG_SPIRAM_USE_HEAP=y
|
||||
CONFIG_SPIRAM_CACHE_WR=y
|
||||
CONFIG_SPIRAM_CACHE_WR=y
|
||||
|
||||
# PSRAM Configuration
|
||||
CONFIG_SPIRAM_BOOT_INIT=y
|
||||
CONFIG_SPIRAM_IGNORE_NOTFOUND=n
|
||||
CONFIG_SPIRAM_USE_MEMMAP=y
|
||||
CONFIG_SPIRAM_USE_NOINIT=n
|
||||
CONFIG_SPIRAM_USE_EMBEDDED_MEM=n
|
||||
CONFIG_SPIRAM_USE_CAPS_ALLOC=y
|
||||
CONFIG_SPIRAM_USE_MALLOC=y
|
||||
CONFIG_SPIRAM_USE_HEAP=y
|
||||
CONFIG_SPIRAM_MEMTEST=y
|
||||
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384
|
||||
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768
|
||||
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
|
||||
CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y
|
||||
CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=n
|
||||
|
||||
# Performance Settings
|
||||
CONFIG_ESP32S3_RTOS_INT_NUM=0
|
||||
CONFIG_ESP32S3_TRACEMEM_RESERVE_DRAM=0x0
|
||||
|
||||
# Flash Configuration
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHMODE_DIO=y
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
|
||||
|
||||
# Partition Table
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_single_app.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_single_app.csv"
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0x8000
|
||||
|
||||
# UART Configuration
|
||||
CONFIG_ESP_CONSOLE_UART_DEFAULT=y
|
||||
CONFIG_ESP_CONSOLE_UART_NUM=0
|
||||
CONFIG_ESP_CONSOLE_UART_TX_GPIO=43
|
||||
CONFIG_ESP_CONSOLE_UART_RX_GPIO=44
|
||||
|
||||
# GPIO Configuration
|
||||
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
|
||||
|
||||
# FreeRTOS Configuration
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
# Log Configuration
|
||||
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
|
||||
CONFIG_LOG_MAXIMUM_LEVEL=3
|
||||
|
||||
# Component Configuration
|
||||
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32
|
||||
CONFIG_LWIP_UDP_RECVMBOX_SIZE=6
|
||||
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=3072
|
||||
|
||||
# WiFi Configuration
|
||||
CONFIG_ESP32_WIFI_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y
|
||||
CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752
|
||||
|
||||
# Bluetooth Configuration (ESP32-S3 doesn't have Bluetooth Audio)
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
CONFIG_BT_CLASSIC_ENABLED=y
|
||||
CONFIG_BT_SCO_ENABLED=y
|
||||
CONFIG_BT_A2DP_ENABLE=y
|
||||
CONFIG_BT_BLE_ENABLED=y
|
||||
|
||||
# ADC Configuration
|
||||
CONFIG_ADC_CAL_EFUSE_TP_ENABLE=n
|
||||
CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=n
|
||||
CONFIG_ADC_CAL_LUT_ENABLE=n
|
||||
|
||||
# I2C Configuration
|
||||
CONFIG_I2C_ENABLE_DEBUG_LOG=n
|
||||
|
||||
# SPI Configuration
|
||||
CONFIG_SPI_MASTER_IN_IRAM=y
|
||||
CONFIG_SPI_SLAVE_IN_IRAM=y
|
||||
|
||||
# Touch Configuration
|
||||
CONFIG_TOUCH_PAD_SLEEP_CYCLE=2000
|
||||
|
||||
# FAT Filesystem
|
||||
CONFIG_FATFS_LAZY_LOCK=y
|
||||
CONFIG_FATFS_API_IN_IRAM=y
|
||||
|
||||
# Event Loop Library
|
||||
CONFIG_EVENT_LOOP_PROFILING=n
|
||||
|
||||
# Ethernet
|
||||
CONFIG_ETH_USE_ESP32_EMAC=n
|
||||
|
||||
# HTTP Server
|
||||
CONFIG_HTTPD_WS_SUPPORT=y
|
||||
|
||||
# JSON Parser
|
||||
CONFIG_JSON_ENABLE_DEBUG=n
|
||||
|
||||
# MDNS
|
||||
CONFIG_MDNS_ENABLE_DEBUG=n
|
||||
|
||||
# MQTT
|
||||
CONFIG_MQTT_PROTOCOL_311=y
|
||||
CONFIG_MQTT_TRANSPORT_SSL=y
|
||||
CONFIG_MQTT_TRANSPORT_WEBSOCKET=y
|
||||
|
||||
# OpenSSL
|
||||
CONFIG_OPENSSL_DEBUG=n
|
||||
|
||||
# Protobuf-c
|
||||
CONFIG_PROTOBUF_C_ENABLE_DEBUG=n
|
||||
|
||||
# WebSockets
|
||||
CONFIG_WS_BUFFER_SIZE=1024
|
||||
|
||||
# NVS
|
||||
CONFIG_NVS_ENCRYPTION=n
|
||||
CONFIG_NVS_ASSERT_ERROR_CHECK=n
|
||||
Reference in New Issue
Block a user