А есть ли у ILI9488 режим 16-разрядного цвета?

не знаю подойдет ли эта библиотека на stm а не на esp
https://github.com/Bodmer/TJpg_Decoder но может это будет удобнее…
для более быстрого вывода я вроде использовал больше озу и несколько ядер esp, сначала файлы грузились в озу, а затем еще один раздел озу который отвечал только за копирование из другого раздела, и вывод на дисплей файлов частями, давал как минимум 3 fps на экране 160x128 объяснять не умею, правильные названия слов не знаю))) может пригодится такое описание…

можно еще предзагрузку в psram делать, а от туда частями c помощью озу быстро считывать и выводить…
может вам все таки захочется на esp это реализовать…

Говорить о “более быстром выводе” можно лишь, анализируя время выполнения отдельных операций и, соответственно, приводя конкретные цифры.

Объем картинки 480*320 составляет 460800 байтов, а каков объем ОЗУ контроллера? Для эфективной работы объем ОЗУ должен быть больше минимум вдвое: один буфер для чтения данных с SD, а другой - экранный буфер.

Вы мое сообщение читали? Затраты процессорного времени составляют 1-2%, если распараллелить работу между ядрами, они могут сократиться до 0.5-1%. Это совсем не то место, которое нуждается в оптимизации.

Для экрана 480*320 это пропорционально составит 0.4 fps или один кадр за 2.5 секунды, т.е. то же самое, что и у меня с учетом загрузке файла с SD. Если рассматривать только отображение, то 9 fps.

То есть к двум операциям с SPI (чтение с SD и отсылка на экран) добавим еще две: запись в psram и чтение из psram. Прогнозируемое уменьшение fps - в 2 раза.

Надо добавить: “арифметикой не владею”.

На самом деле основная проблема - библиотека SD. Уже не первый раз в нее упираюсь, надо будет как-то попытаться соптимизировать работу с SD, но для этого подобрать подходящую библиотеку. Поставляемая по умолчанию SD для этого мало подходит.

andriano в третий раз заказываю esp32 в описании указанно есть psram, а приезжает без нее)))
за счет того что хватает озу выходит 7-15 fps c psram похоже так никогда и не выложу код)))

#include <TFT_eSPI.h>
#include <TJpg_Decoder.h>
#include <SPI.h>
#include <SD.h>

using namespace fs;

#define SD_CS 5
#define TFT_BL 12
#define TOTAL_FRAMES 420

#define SCREEN_W 128
#define SCREEN_H 160
#define IMG_W 113
#define IMG_H 164
#define POS_X ((SCREEN_W - IMG_W) / 2)
#define POS_Y ((SCREEN_H - IMG_H) / 2)

TFT_eSPI tft = TFT_eSPI();

uint16_t* frameBuffer = nullptr;
uint16_t currentFrame = 0;

// Буфер для JPEG
uint8_t* jpgBuffer = nullptr;
size_t jpgSize = 0;

bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap) {
  if (y >= tft.height()) return 0;
  
  if (frameBuffer != nullptr) {
    uint16_t* dest = frameBuffer + (y * SCREEN_W + x);
    uint16_t* src = bitmap;
    for (int row = 0; row < h; row++) {
      memcpy(dest + row * SCREEN_W, src + row * w, w * 2);
    }
  }
  return 1;
}

bool loadJpgToMemory(uint16_t frameNum) {
  char filename[20];
  sprintf(filename, "/%03d.jpg", frameNum);
  
  File file = SD.open(filename);
  if (!file) return false;
  
  jpgSize = file.size();
  
  if (jpgBuffer != nullptr) {
    free(jpgBuffer);
    jpgBuffer = nullptr;
  }
  
  jpgBuffer = (uint8_t*)malloc(jpgSize);
  if (jpgBuffer == nullptr) {
    file.close();
    return false;
  }
  
  size_t bytesRead = file.read(jpgBuffer, jpgSize);
  file.close();
  
  return bytesRead == jpgSize;
}

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("\n=== ST7735 START ===");
  
  pinMode(TFT_BL, OUTPUT);
  digitalWrite(TFT_BL, HIGH);
  
  tft.init();
  tft.setRotation(0);
  tft.setSwapBytes(true);
  tft.fillScreen(TFT_BLACK);
  
  if (!SD.begin(SD_CS, SPI, 4000000)) {
    Serial.println("SD Error!");
    while(1);
  }
  Serial.println("SD mounted!");
  
  TJpgDec.setJpgScale(1);
  TJpgDec.setCallback(tft_output);
  
  if (!SD.exists("/001.jpg")) {
    Serial.println("File 001.jpg not found!");
    while(1);
  }
  
  size_t bufferSize = SCREEN_W * SCREEN_H * sizeof(uint16_t);
  frameBuffer = (uint16_t*)malloc(bufferSize);
  if (frameBuffer == nullptr) {
    Serial.println("No memory!");
    while(1);
  }
  
  if (!loadJpgToMemory(1)) {
    Serial.println("Failed to load first jpg!");
    while(1);
  }
  
  memset(frameBuffer, 0, bufferSize);
  TJpgDec.drawJpg(POS_X, POS_Y, jpgBuffer, jpgSize);
  tft.pushImage(0, 0, SCREEN_W, SCREEN_H, frameBuffer);
  
  Serial.println("Started!");
}

void loop() {
  char filename[20];
  sprintf(filename, "/%03d.jpg", currentFrame + 1);
  
  if (loadJpgToMemory(currentFrame + 1)) {
    memset(frameBuffer, 0, SCREEN_W * SCREEN_H * 2);
    TJpgDec.drawJpg(POS_X, POS_Y, jpgBuffer, jpgSize);
    tft.pushImage(0, 0, SCREEN_W, SCREEN_H, frameBuffer);
  }
  
  currentFrame++;
  if (currentFrame >= TOTAL_FRAMES) {
    currentFrame = 0;
  }
}

User_Setup.h библиотеки #include <TFT_eSPI.h> на всякий случай оставлю…

#ifndef USER_SETUP_H
#define USER_SETUP_H

// === Настройка дисплея ST7735 ===
#define ST7735_DRIVER
#define TFT_WIDTH  128
#define TFT_HEIGHT 160
#define ST7735_REDTAB160x128  // Для дисплеев с красной платой (128x160)


// Вариант 1: BGR (чаще для ST7735)
#define TFT_RGB_ORDER TFT_BGR
#define TFT_INVERSION_ON  // Включить инверсию

// Вариант 2: RGB
#define TFT_RGB_ORDER TFT_BGR//RGB
#define TFT_INVERSION_OFF

// === Пины SPI (HSPI) ===
#define TFT_MOSI  13  // GPIO13 (D7) esp8266
#define TFT_SCLK  14  // GPIO14 (D5)
#define TFT_CS    15  // GPIO15 (D8) - Chip Select
#define TFT_DC     2  // GPIO2  (D4) - Data/Command
#define TFT_RST    4  // GPIO4  (D2) - Reset (можно подключить к +3.3V, если есть кнопка Reset)
#define TFT_BL    12  // GPIO12 (D6) - Подсветка (PWM)

// === Настройки SPI ===
//#define SPI_FREQUENCY  4000000  // Максимум для ST7735
//#define SPI_READ_FREQUENCY  40000000
#define SPI_FREQUENCY  40000000  // 40 MHz - максимум для ST7735
// или
#define SPI_FREQUENCY  27000000  // 27 MHz - стабильнее
// === Подсветка ===
#define TFT_BL   12  // Управление подсветкой через PWM

// === Настройки SD-карты (VSPI) ===
#define SD_CS    5  // GPIO5
#define SD_SCLK 18  // GPIO18
#define SD_MOSI 23  // GPIO23
#define SD_MISO 19  // GPIO19

// === Поддержка шрифтов ===
#define LOAD_GLCD    // Стандартный шрифт 5x7
#define LOAD_FONT2   // Шрифт 8x16
#define LOAD_FONT4   // Шрифт 16x26
#define LOAD_GFXFF   // Гибкие шрифты
#define SMOOTH_FONT  // Сглаженные шрифты

// === Поддержка JPEG с SD ===
#define LOAD_JPG

#endif

может все таки захочется на ней собрать)))

Честно говоря, мне сложно представить проект, в котором WiFi сочеталась бы с дисплеем высокого разрешения. Эта ниша уже прочно занята смартфонами, планшетами, etc.

я черпаю вдохновение в заказах в сети))) с вау эффектом наверное нечего не придумать… я додумался только до того что бы часы сделать, время из сети (выводится с помощью фото цифр) + анимация ниже, 10 ползунков которые указывают какую анимацию выводить по очереди…

кстате совершенно случайно обнаружил баг, каждое второе, четвертое, 6, 8 воспроизведение зацикленной анимации выводится 34-36 секунд, а каждое 1,3,5 и т.д. за 20-22 секунды и просто в тупике, скорость может быть гораздо больше, и обидно как то иногда смотреть медленную анимацию
вот последний код, библиотека если что тут https://github.com/Bodmer/TJpg_Decoder лично я просто в тупике… если есть время, и для вас причина очевидна, подскажите пж в чем она))) и можно выводить еще быстрее кажется…

#include <TFT_eSPI.h>
#include <TJpg_Decoder.h>
#include <SPI.h>
#include <SD.h>

using namespace fs;

#define SD_CS 5
#define TFT_BL 12
#define TOTAL_FRAMES 420

#define SCREEN_W 128
#define SCREEN_H 160
#define IMG_W 113
#define IMG_H 164
#define POS_X ((SCREEN_W - IMG_W) / 2)
#define POS_Y ((SCREEN_H - IMG_H) / 2)

TFT_eSPI tft = TFT_eSPI();

uint16_t* frameBuffer = nullptr;
uint16_t currentFrame = 0;

uint8_t* jpgBuffer = nullptr;
size_t jpgSize = 0;

bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap) {
  if (y >= tft.height()) return 0;
  
  if (frameBuffer != nullptr) {
    uint16_t* dest = frameBuffer + (y * SCREEN_W + x);
    uint16_t* src = bitmap;
    for (int row = 0; row < h; row++) {
      memcpy(dest + row * SCREEN_W, src + row * w, w * 2);
    }
  }
  return 1;
}

bool loadJpgToMemory(uint16_t frameNum) {
  char filename[20];
  sprintf(filename, "/%03d.jpg", frameNum);
  
  File file = SD.open(filename);
  if (!file) return false;
  
  jpgSize = file.size();
  
  if (jpgBuffer != nullptr) {
    free(jpgBuffer);
    jpgBuffer = nullptr;
  }
  
  jpgBuffer = (uint8_t*)malloc(jpgSize);
  if (jpgBuffer == nullptr) {
    file.close();
    return false;
  }
  
  size_t bytesRead = file.read(jpgBuffer, jpgSize);
  file.close();
  
  return bytesRead == jpgSize;
}

// ★ ФУНКЦИЯ СБРОСА ДЕКОДЕРА ★
void resetDecoder() {
  // Полный сброс декодера
  TJpgDec.setJpgScale(1);
  TJpgDec.setCallback(tft_output);
  
  // Небольшая пауза для стабилизации
  delay(5);
  
  Serial.println("Decoder reset!");
}

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("\n=== ST7735 FIXED ===");
  
  pinMode(TFT_BL, OUTPUT);
  digitalWrite(TFT_BL, HIGH);
  
  tft.init();
  tft.setRotation(0);
  tft.setSwapBytes(true);
  tft.fillScreen(TFT_BLACK);
  
  if (!SD.begin(SD_CS, SPI)) {
    Serial.println("SD Error!");
    while(1);
  }
  Serial.println("SD mounted!");
  
  TJpgDec.setJpgScale(1);
  TJpgDec.setCallback(tft_output);
  
  if (!SD.exists("/001.jpg")) {
    Serial.println("File 001.jpg not found!");
    while(1);
  }
  
  size_t bufferSize = SCREEN_W * SCREEN_H * sizeof(uint16_t);
  frameBuffer = (uint16_t*)malloc(bufferSize);
  if (frameBuffer == nullptr) {
    Serial.println("No memory!");
    while(1);
  }
  
  if (!loadJpgToMemory(1)) {
    Serial.println("Failed to load first jpg!");
    while(1);
  }
  
  memset(frameBuffer, 0, bufferSize);
  TJpgDec.drawJpg(POS_X, POS_Y, jpgBuffer, jpgSize);
  tft.pushImage(0, 0, SCREEN_W, SCREEN_H, frameBuffer);
  
  Serial.println("Started!");
}

void loop() {
  char filename[20];
  sprintf(filename, "/%03d.jpg", currentFrame + 1);
  
  if (loadJpgToMemory(currentFrame + 1)) {
    memset(frameBuffer, 0, SCREEN_W * SCREEN_H * 2);
    TJpgDec.drawJpg(POS_X, POS_Y, jpgBuffer, jpgSize);
    tft.pushImage(0, 0, SCREEN_W, SCREEN_H, frameBuffer);
  }
  
  currentFrame++;
  
  // ★ КОГДА АНИМАЦИЯ ЗАКАНЧИВАЕТСЯ - ПОЛНЫЙ СБРОС ★
  if (currentFrame >= TOTAL_FRAMES) {
    currentFrame = 0;
    
    // Освобождаем буфер
    if (jpgBuffer != nullptr) {
      free(jpgBuffer);
      jpgBuffer = nullptr;
    }
    
    // Сбрасываем декодер
    resetDecoder();
    
    // Принудительно очищаем экран
    tft.fillScreen(TFT_BLACK);
    
    // Загружаем первый кадр заново
    if (loadJpgToMemory(1)) {
      memset(frameBuffer, 0, SCREEN_W * SCREEN_H * 2);
      TJpgDec.drawJpg(POS_X, POS_Y, jpgBuffer, jpgSize);
      tft.pushImage(0, 0, SCREEN_W, SCREEN_H, frameBuffer);
    }
    
    Serial.println("=== CYCLE RESTART ===");
    
    // Небольшая задержка для стабилизации
    delay(10);
  }
}