You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I am using SPI_FREQUENCY 80E6 and SPI_READ_FREQUENCY 60E6 on a recently purchased Waveshare ESP32S3-GEEK board, which has an ST7789 240x135 RGB565 TFT LCD.
By leveraging FreeRTOS, I created a task that continuously refreshes the TFT LCD at a stable 85 Hz. Due to vTask timing precision being limited to 1 ms, the actual refresh rate becomes 83.33 Hz.
Additionally, I implemented an SD card streaming system that reads an RGB565 video file at 60 FPS. The video was generated using OpenCV, where I converted each frame to PNG, then transformed them into RGB565 format and merged them into a single video.bin file.
Surprisingly, I am able to consistently achieve this high frame rate without using DMA. I am unsure why the performance remains so stable.
I will include code snippets and a video demonstration for reference.
Would love to hear thoughts or explanations on this!
IMG_5774-2.mp4
code
Setup303_WaveShare_ESP32S3_GEEK.h
// ST7789 135 x 240 display
#define USER_SETUP_ID 303
#ifdef ILI9341_DRIVER
#undef ILI9341_DRIVER
#endif
#define ST7789_DRIVER // Configure all registers
#define TFT_WIDTH 135
#define TFT_HEIGHT 240
#define CGRAM_OFFSET // Library will add offsets required
#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
#define TFT_INVERSION_ON
// Generic ESP32 setup
#define TFT_MOSI 11
#define TFT_SCLK 12
#define TFT_CS 10
#define TFT_DC 8
#define TFT_RST 9
//#define TFT_BL 7
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
#define SMOOTH_FONT
#define USE_HSPI_PORT
//#define ESP32_DMA
//#define SPI_FREQUENCY 27000000
#define SPI_FREQUENCY (int)80E6
#define SPI_READ_FREQUENCY (int)60E6
libfb.cpp
//#define USE_DMA
// .........
void libfb::fb_xinit()
{
if (!fb_is_init)
{
xTaskCreatePinnedToCore([](void *param) { auto *fbInstance = static_cast<libfb*>(param); fbInstance->_fbUpdate(param);}, "FB_UPDATE", 8192, this, 1, &_fb_update_handel, 0);
fb_is_init=true;
}
else
{
log_e("Fr4M3 BuFf3R 1s 4lR3aDy 1N1t !");
}
}
// ........
void libfb::fb_disp_image(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t* ptr, uint16_t* data)
{
if (x < 0 || y < 0 || (x + w) > TFT__WIDTH || (y + h) > TFT__HEIHT)
{
return; // Exit if the area is out of bounds
}
int32_t data_pos = 0;
for (int32_t i = 0; i < h; i++)
{
for (int32_t j = 0; j < w; j++)
{
// Calculate the 1D position in the image_data buffer
int32_t pos = (y + i) * TFT__WIDTH + (x + j);
ptr[pos] = data[data_pos++];
}
}
}
//......
void libfb::_fbUpdate(void* parameter)
{
if (this == nullptr)
{
log_e("this->nullptr!!!");
return;
}
int64_t prevTime = esp_timer_get_time(); // Get initial timestamp (µs)
const int TARGET_FPS = 85; // Target frame rate
int64_t FRAME_TIME_US = (1000000.0 / TARGET_FPS); // Target frame time (µs)
for(;;)
{
int64_t currentTime = esp_timer_get_time();
int64_t elapsedTime = currentTime - prevTime;
if (elapsedTime >= FRAME_TIME_US)
{
prevTime = currentTime;
fb_update_frame_time = elapsedTime;
if (rel_frameBuffer != nullptr && frameBuffer != nullptr)
{
#ifdef USE_DMA
uint32_t frameStart = millis(); // Start frame timer
LCD.startWrite();
//LCD.dmaWait();
//LCD.setSwapBytes(true);
LCD.pushImageDMA(0, 0, 240, 135, frameBuffer); // Initiate DMA - blocking only if last DMA is not complete
LCD.endWrite();
uint32_t frameDuration = millis() - frameStart;
frame_delay_time = frameDuration;
// The DMA transfer of image block to the TFT is now in progress...
#else
// Non-DMA blocking alternative
uint32_t frameStart = millis(); // Start frame timer
LCD.pushImage(0, 0, 240, 135, frameBuffer); // Blocking, so only returns when image block is drawn
uint32_t frameDuration = millis() - frameStart;
frame_delay_time = frameDuration;
#endif
}
}
vTaskDelay(1);
}
}
main.cpp
TaskHandle_t TEST_FB_VIDEO_TESK1_Handle = NULL;
// ......
void setup() {
fb.fb_xinit();
xTaskCreate(TEST_FB_VIDEO_TESK1, "TEST_TASK_1", 66600, NULL, 1, &TEST_FB_VIDEO_TESK1_Handle);
//....
}
void TEST_FB_VIDEO_TESK1(void* parameter)
{
int64_t prevTime = esp_timer_get_time(); // Get initial timestamp (µs)
const int TARGET_FPS = 60; // Target frame rate
int64_t FRAME_TIME_US = (1000 / TARGET_FPS)*1000; // Target frame time (µs)
//const int TARGET_FPS = 60; // Target frame rate
//const int FRAME_TIME_MS = 1000 / TARGET_FPS; // Target frame time (ms)
// Allocate buffer once to avoid malloc/free overhead
uint16_t *buffer = (uint16_t*) heap_caps_malloc(TFT__WIDTH * TFT__HEIHT * sizeof(uint16_t), MALLOC_CAP_SPIRAM);
if (!buffer) {
SerialUSB.println("Memory allocation failed!");
vTaskDelete(NULL);
return;
}
for(;;)
{
failSD:
// Wait for SD card insertion
HotPlugSD.checkCardStatus();
while (!HotPlugSD.isCardInserted()) {
SerialUSB.println("SD card missing. Halting...");
vTaskDelay(pdMS_TO_TICKS(500)); // Check every 500ms
}
//uint32_t prevTime = millis(); // Get initial timestamp (µs)
fs::File video_file = HotPlugSD.open("/video.bin", "r");
if (!video_file)
{
SerialUSB.println("Failed to open video file. Retrying...");
vTaskDelay(pdMS_TO_TICKS(500)); // Retry after delay
continue;
}
int i=0;
while (video_file.available())
{
/* code */
int64_t currentTime = esp_timer_get_time();
int64_t elapsedTime = currentTime - prevTime;
if (elapsedTime >= FRAME_TIME_US)
{
prevTime = currentTime;
int bytesRead = video_file.read((uint8_t *)buffer, TFT__WIDTH * TFT__HEIHT * sizeof(uint16_t)); // Fast read
if (bytesRead != TFT__WIDTH * TFT__HEIHT * sizeof(uint16_t)) goto failSD;
fb.fb_disp_image(0, 0, 240, 135, fb.frameBuffer, buffer); // Display frame
// Measure and print actual FPS every 10 frames
if (i % 10 == 0)
{
SerialUSB.printf("Actual SOFT FPS: %lf\t TFTLCD PUSH TIME: %u ms\t TFTLCD UPDATE TIME: %lld us\t TFTLCD ACTUAL HZ: %lf\n", 1000000.0 / elapsedTime, fb.frame_delay_time, fb.fb_update_frame_time, 1000000.0 / fb.fb_update_frame_time);
}
// Check if SD card was removed mid-playback
if (!HotPlugSD.isCardInserted()) {
SerialUSB.println("SD card removed. Halting playback.");
video_file.close();
while (!HotPlugSD.isCardInserted())
{
SerialUSB.println("Waiting for SD card...");
vTaskDelay(pdMS_TO_TICKS(500));
}
if (!reinitializeSD())
{
SerialUSB.println("Failed to reinitialize SD card.");
continue; // Restart loop
}
}
i++;
}
vTaskDelay(1);
//ets_delay_us(1);
}
video_file.close();
vTaskDelay(pdMS_TO_TICKS(10));
}
heap_caps_free(buffer);
vTaskDelete(NULL);
}
The text was updated successfully, but these errors were encountered:
I am using
SPI_FREQUENCY 80E6
andSPI_READ_FREQUENCY 60E6
on a recently purchased Waveshare ESP32S3-GEEK board, which has an ST7789 240x135 RGB565 TFT LCD.By leveraging FreeRTOS, I created a task that continuously refreshes the TFT LCD at a stable 85 Hz. Due to vTask timing precision being limited to 1 ms, the actual refresh rate becomes 83.33 Hz.
Additionally, I implemented an SD card streaming system that reads an RGB565 video file at 60 FPS. The video was generated using OpenCV, where I converted each frame to PNG, then transformed them into RGB565 format and merged them into a single
video.bin
file.Surprisingly, I am able to consistently achieve this high frame rate without using DMA. I am unsure why the performance remains so stable.
I will include code snippets and a video demonstration for reference.
Would love to hear thoughts or explanations on this!
IMG_5774-2.mp4
code
Setup303_WaveShare_ESP32S3_GEEK.h
libfb.cpp
main.cpp
The text was updated successfully, but these errors were encountered: