Oficina de animação: guia de otimização do XIAO ESP32-S3 e LVGL
Bem-vindo à Oficina de Animação! Este tutorial vai guiá-lo pelo processo de pegar animações SVG básicas e otimizá‑las para o ESP32-S3. As otimizações irão conduzi‑lo por cinco fases que mostram melhorias desde uma taxa de quadros travada em 7–9 FPS até uma fluidez de 30 FPS.
Nesta oficina, você implementará um aplicativo que renderiza e anima três assets vetoriais: um Beija-flor, um Guaxinim e uma Baleia. Você aprenderá a lidar com renderização SVG complexa em hardware com recursos limitados, passando por fases de otimização progressiva.

Beija-flor

Guaxinim

Baleia
O código de exemplo usa um Seeed Studio XIAO ESP32-S3 Plus e um XIAO Round Display.
🌐 O ecossistema
Este projeto aproveita uma poderosa pilha open source projetada para gráficos embarcados de alto desempenho:
- ESP32-S3: Um MCU dual-core com instruções vetoriais e 8MB de Octal PSRAM.
- ESP-IDF: O framework oficial de desenvolvimento para SoCs da Espressif.
- LVGL: A biblioteca de gráficos embarcados open source mais popular.
lvgl_cpp: Um wrapper moderno em C++20 para LVGL que fornece segurança de tipos e abstrações idiomáticas para objetos, animações e displays.
Primeiros passos
Pré-requisitos
Antes de mergulhar no código, certifique‑se de ter um ecossistema de desenvolvimento para trabalhar com ESP32. Este tutorial assume que você tenha as seguintes ferramentas instaladas:
No entanto, não há nada neste projeto que o impeça de usar outras ferramentas como o PlatformIO ou a interface de linha de comando do ESP-IDF.
Etapa 0: A verificação "Hello World" (crucial)
Se você é novo no ecossistema ESP32 com ESP-IDF, não pule esta etapa.
- Abra o VS Code e pressione
F1. - Digite
ESP-IDF: Show Examples Projects. - Selecione
get-started->blink(ouhello_world). - Compile, grave (Flash) e monitore o projeto no seu dispositivo.
Por quê? Isso verifica se sua toolchain, drivers USB e conexão de hardware estão totalmente funcionais antes de introduzirmos a complexidade de uma biblioteca gráfica em C++.
Etapa 1a: Início rápido (clonar a oficina)
Se você prefere não começar do zero, pode clonar o repositório completo da oficina, que já vem pré-configurado com todos os componentes e configurações.
git clone https://github.com/pedapudi/lvgl_workshop.git
cd lvgl_workshop
git submodule update --init --recursive
Se você escolher este caminho, pode pular para a Etapa 3 (Configuração) para verificar suas definições ou ir direto para a compilação.
Etapa 1b: Criar um novo projeto (do zero)
Se você quiser construir o projeto por conta própria:
- Abra
ESP-IDF: Show Examples Projectsnovamente. - Escolha
get-started->sample_project. - Clique em "Create project using example blink".
Etapa 2: Instalar lvgl_cpp
Instalaremos a biblioteca como um componente local. No terminal da raiz do seu projeto:
mkdir -p components
cd components
git submodule add https://github.com/pedapudi/lvgl_cpp.git lvgl_cpp
git submodule update --init --recursive
Observação:
lvgl_cppinclui um arquivoidf_component.ymlque irá automaticamente buscar a versão correta delvgl/lvgl(v9.x) no IDF Component Registry durante a primeira compilação. Você não precisa instalar o LVGL manualmente.
Etapa 3: Configuração
Abra o menu de configuração do projeto para habilitar C++20 e definições ideais:
- Pressione
F1e executeESP-IDF: SDK Configuration Editor (Menuconfig). - Navegue até
Compiler options->C++ Language Standarde selecioneC++20(ouGNU++20). - (Opcional, mas recomendado) Defina
Compiler optimization levelcomoOptimize for performance (-O2).
🛠️ Como seguir este guia
Esta oficina usa um "limitador de software" para simular limitações de hardware sem forçá‑lo a recompilar o bootloader constantemente. Você pode alternar os níveis de implementação de duas maneiras:
- O jeito da oficina: Use
idf.py menuconfig->Animation Workshoppara alternar entre as fases 1–5. - O jeito manual: Modifique
#define WORKSHOP_PHASEemmain/workshop_config.h.
📊 Visão geral das fases
A oficina progride por quatro estágios de otimização, passando de uma implementação funcional ingênua para uma utilização de PSRAM em nível avançado.
| Fase | Título | Otimizações principais | Estratégia de Buffer | Modo de Renderização |
|---|---|---|---|---|
| Fase 1 | Linha de base | CPU de 160MHz / SPI de 20MHz | 1x Frame Completo (Interno) | Atualização Completa |
| Fase 2 | Fundamentos | CPU de 240MHz / SPI de 80MHz | 1x Frame Completo (Interno) | Atualização Completa |
| Fase 3 | Paralelismo | Double Buffering | 2x Faixa Parcial (Interno) | Atualização Parcial |
| Fase 4 | Avançado | Octal PSRAM / SIMD | 2x Frame Completo (PSRAM) | Atualização Parcial |
| Fase 5 | Nativo | Driver Nativo / SWAR SIMD | 2x Parcial Grande (Interno) | Atualização Parcial |
Fase 1: A linha de base (implementação ingênua)
Objetivo: Exibir um SVG com configuração mínima.
Nesta fase, focamos na correção funcional. Usamos o mecanismo ThorVG do LVGL para decodificar e renderizar caminhos SVG. No entanto, como ainda não otimizamos o sistema, a CPU é lenta (160MHz) e o modo de renderização está definido como Full Refresh (redesenhando cada pixel a cada frame), causando uma sobrecarga significativa.
⚙️ Configuração do ESP-IDF
Para atingir esta linha de base em um projeto de produção, você configuraria:
- Frequência da CPU:
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160emsdkconfig. - Pilha da tarefa LVGL: Definida como um parâmetro na sua chamada
xTaskCreate(tipicamente8192). - Barramento do display: Defina
.pclk_hz = 20 * 1000 * 1000(20MHz) na sua structesp_lcd_panel_io_spi_config_t. - Otimização:
CONFIG_COMPILER_OPTIMIZATION_DEFAULT(-Og).
💻 Implementação
Configurar o hardware e a exibição de SVG na Fase 1 exige orquestrar o driver do display e a camada de portabilidade do LVGL.
Inicialização de hardware e porta (app_main):
// Initialize the GC9A01 SPI display
Gc9a01 display_hw(display_cfg);
display_hw.init();
// Initialize the LVGL porting layer with SRAM-only buffers
LvglPort::Config lvgl_config;
lvgl_config.malloc_caps = MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL;
lvgl_config.double_buffered = false;
LvglPort lvgl_port(lvgl_config);
lvgl_port.init(display_hw.get_panel_handle(), display_hw.get_io_handle());
Lógica de exibição do SVG:
// 1. skip SVG header metadata to find the XML start tag
const char* raw_svg_ptr = hummingbird_svg;
while (*raw_svg_ptr && *raw_svg_ptr != '<') raw_svg_ptr++;
// 2. create an image descriptor for ThorVG
static lvgl::ImageDescriptor bird_dsc(75, 75, LV_COLOR_FORMAT_RAW,
(const uint8_t*)raw_svg_ptr,
strlen(raw_svg_ptr) + 1);
// 3. display the SVG on an image object
auto hummingbird = std::make_unique<lvgl::Image>(parent);
hummingbird->set_src(bird_dsc).center();
Resultado: ~9 FPS. O beija-flor estático é renderizado com sucesso e a animação do guaxinim é executada sem travar (embora continue truncada). No entanto, a pilha padrão de 8KB está perigosamente perto de estourar ao fazer o scaling de caminhos complexos.
Fase 2: Fundamentos de hardware
Objetivo: Maximizar as frequências brutas do ESP32-S3.
A renderização de SVG é um "Problema de Matemática". Ao aumentar a frequência da CPU, damos ao mecanismo vetorial mais ciclos para calcular curvas de Bézier.
⚡ A estratégia
- Impulso na CPU: Aumentar a frequência de 160MHz para 240MHz.
- Overclock do SPI: Aumentar a velocidade da via do display de 20MHz para 80MHz.
- Poder do compilador: Habilitar otimizações de desempenho -O3 para acelerar o mecanismo de matemática vetorial.
⚙️ Configuração do ESP-IDF
- Velocidade da CPU:
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240emsdkconfig. - Barramento do display:
.pclk_hz = 80 * 1000 * 1000(80MHz) na sua configuração SPI.
Observação: 80MHz é o limite absoluto para o barramento SPI do S3. Valores maiores não são suportados pelo hardware, e velocidades menores causam "rasgos" visíveis, já que o barramento não consegue acompanhar as atualizações de frame.
- Nível de otimização: Defina
CONFIG_COMPILER_OPTIMIZATION_PERF=yna sua configuração para habilitar-O3. - Correção de bytes: No seu
flush_cb, implemente um loop para trocar os bytes de Little-Endian (CPU) para Big-Endian (LCD):
void LvglPort::flush_cb(lv_display_t* disp, const lv_area_t* area, uint8_t* px_map) {
auto* port = (LvglPort*)lv_display_get_user_data(disp);
uint16_t* buf = (uint16_t*)px_map;
uint32_t len = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1);
for (uint32_t i = 0; i < len; i++) {
uint16_t color = buf[i];
buf[i] = (color << 8) | (color >> 8);
}
esp_lcd_panel_draw_bitmap(port->panel_handle_, area->x1, area->y1,
area->x2 + 1, area->y2 + 1, buf);
}
Resultado: ~15 FPS. O movimento é visivelmente mais suave em comparação com a Fase 1.
Fase 3: Lógica paralela e double buffering
Objetivo: Eliminar o screen tearing e desacoplar a CPU do display.
⚡ A estratégia
- Double buffering: Alocamos dois buffers separados. Enquanto o hardware DMA (Direct Memory Access) está enviando o Buffer A para a tela, a CPU pode imediatamente começar a desenhar no Buffer B.
- Aumento de stack: Aumentamos a stack da task do LVGL para 64KB. Gráficos vetoriais usam recursão; uma stack de 8KB vai estourar e travar ao escalar recursos complexos.
⚙️ Configuração ESP-IDF
- DMA habilitado: Garanta que
MALLOC_CAP_DMAseja usado durante a alocação do buffer. - Buffers LVGL: Ao chamar
lv_display_set_buffers, forneça dois ponteiros em vez de um. - Modo de buffer: Defina
LV_DISPLAY_RENDER_MODE_PARTIAL.
💻 Implementação
Mover para a Fase 3 exige habilitar o double buffering e aumentar a stack da task para evitar stack overflows durante a escala de SVGs complexos.
Alocação de double buffer:
// Allocate two strike buffers in internal memory
lvgl_config.double_buffered = true;
lvgl_config.render_mode = LV_DISPLAY_RENDER_MODE_PARTIAL;
// Use small strip buffers (usually 1/10th or 1/20th of the screen)
// to fit both buffers in fast internal SRAM.
lvgl_config.full_frame = false;
Aumento de stack seguro para recursão:
// Increasing from 8KB (Standard) to 64KB (Vector-Safe)
lvgl_config.task_stack_size = 65536;
Resultado: ~9 FPS (regressão!).
🧠 Análise profunda: Penalidade de tiling
Por que adicionar paralelismo às vezes torna a animação mais lenta?
- Fase 2 (Frame Completo): O motor ThorVG calcula o caminho vetorial uma vez por frame para toda a imagem 240x240.
- Fase 3 (Faixas Parciais): Como estamos fazendo double buffering em SRAM limitada, só conseguimos acomodar buffers pequenos (por exemplo, 20 linhas). Isso força o ThorVG a executar seu loop de cálculo 12 vezes (uma para cada faixa) para gerar a imagem completa.
Esse overhead de recalcular a geometria vetorial 12 vezes supera em muito o benefício da transferência DMA paralela, resultando em FPS geral menor (~9 FPS vs ~15 FPS). Esse é o clássico trade-off "Computação vs. Largura de Banda". Resolvemos isso na Fase 4.
Fase 4: Otimização avançada (o segredo dos 26 FPS)
Objetivo: Eliminar o "Tiling Overhead" usando Large Octal PSRAM.
⚡ A estratégia
- Buffers de frame completo: Movemos os buffers para a octal PSRAM de 8MB e os aumentamos para um Frame completo (240x240 pixels).
- O ganho: O ThorVG renderiza o Guaxinim em um único passe. Mesmo que a PSRAM seja um pouco mais lenta que a SRAM, evitar a recalculação 12x é uma grande vitória.
- Intrinsics Xtensa: Substituímos o loop de troca manual por
__builtin_bswap16, uma instrução de hardware que troca bits em um único ciclo.
⚙️ Configuração ESP-IDF
- Inicialização da PSRAM:
CONFIG_SPIRAM=y,CONFIG_SPIRAM_MODE_OCT=y,CONFIG_SPIRAM_SPEED_80M=y. - Capacidades de memória: Use
MALLOC_CAP_DMA | MALLOC_CAP_SPIRAMpara alocar o buffer de 115KB. - Poder do compilador: Habilite
CONFIG_COMPILER_OPTIMIZATION_PERF=yeCONFIG_COMPILER_OPTIMIZATION_LTO=y.
Resultado: ~25 FPS. Animação SVG suave e de alta fidelidade.
🧠 Análise profunda: Intrinsics explícitos de hardware
A parte mais cara do pipeline de renderização é o Flush Callback. Para cada frame, precisamos trocar os bytes de cada pixel (correção de endianness) e inverter as cores (exigência do painel LCD).
Em vez de um loop de matemática C padrão, a Fase 4 usa:
buf[i] = __builtin_bswap16(buf[i]);
__builtin_bswap16: Este é um intrinsic do compilador que informa à CPU para usar uma instrução de hardware dedicada (BE) para trocar bytes em um único ciclo de clock.- ⚠️ NÃO é armadilha (
~): Você pode ver exemplos usando~__builtin_bswap16(). Isso é usado para painéis LCD "Active-Low" que exigem inversão bit a bit para exibir as cores corretamente. Se suas cores parecerem um "negativo de foto", remova o operador~.
Nota: Durante a implementação da Fase 1, usar a inversão ~ em painéis GC9A01 padrão geralmente resulta em cores invertidas. Sempre verifique o nível lógico do seu painel antes de aplicar negação bit a bit no loop de flush.
Fase 5: Arquitetura Nativa (o padrão de 30+ FPS)
Objetivo: Bufferização SRAM "Partial Grande" e bit-swapping SWAR de 32 bits.
Embora a Fase 4 tenha alcançado desempenho à força bruta usando buffers massivos em PSRAM, ela introduziu latência devido aos wait-states da memória externa. A Fase 5 atinge o marco de 30+ FPS ao migrar para uma arquitetura de nível "Mobile": usando buffers de SRAM interna em alta velocidade, mas grandes o suficiente para minimizar o overhead de tiling.
⚡ A estratégia: Arquitetura "Mobile-Grade"
- Bufferização "Partial Grande": Alocamos buffers de 1/2 Tela (240x120) na SRAM interna. Isso alcança o melhor dos dois mundos: maior largura de banda que a PSRAM e apenas um multiplicador de tiling 2x (comparado a 12x na Fase 3).
- Processamento SWAR de 32 bits: Implementamos SIMD-within-a-Register (SWAR) na lógica de flush do driver. Isso nos permite trocar e inverter dois pixels (32 bits) no mesmo tempo que antes levava para processar um pixel (16 bits) usando intrinsics padrão.
- Desfixação de core: Ao remover o pinning de tasks (
tskNO_AFFINITY), permitimos que o escalonador do FreeRTOS sature os dois núcleos da S3—um núcleo pode lidar com a rasterização pesada do ThorVG enquanto o outro atende às interrupções de SPI DMA de alta frequência.
🚀 Aceleração: patch SIMD
Além das otimizações em C++, contamos com um acelerador de hardware crítico: o lvgl_s3_simd_patch.
- Problema: O LVGL padrão usa loops genéricos em C para blending de pixels. No ESP32, isso é lento porque processa um pixel por vez (ineficiente) ou usa otimizações genéricas do compilador que ignoram instruções específicas da plataforma.
- Solução: Injetamos rotinas em Assembly ESP32-S3 escritas à mão (macros como
lv_color_blend_to_rgb565_esp) que aproveitam os recursos SIMD (Single Instruction, Multiple Data) do Xtensa LX7.
Configuração:
Isso é controlado via sdkconfig:
CONFIG_LV_USE_DRAW_SW_ASM=255
Global vs. Fase 5: Essa configuração é globalmente ativa para todas as fases do workshop (1-5). Porém, você provavelmente não notará seu impacto até a Fase 5.
Por quê? Na Fase 4 (PSRAM), a CPU fica constantemente parada aguardando a chegada dos dados a partir do chip de RAM externo. A rotina em assembly é rápida, mas não pode rodar se não tiver dados! Na Fase 5 (SRAM Interna), a largura de banda de memória finalmente é rápida o suficiente para alimentar o pipeline SIMD, permitindo que as otimizações em assembly brilhem.
Como funciona:
- Injeção de header: O componente
lvgl_s3_simd_patchse força no caminho de includes do LVGL. - Overlay de macro: Ele redefine macros padrão (por exemplo,
LV_DRAW_SW_COLOR_BLEND_TO_RGB565) para apontar para nossas funções shim personalizadas. - Mágica do linker: Ele usa flags
-u symbolpara forçar o linker a incluir os objetos em assembly, garantindo que não sejam otimizados fora.
💻 Loop SWAR de 32 bits
O segredo da Fase 5 é a otimização SIMD-Within-A-Register (SWAR).
O processamento padrão de pixels lida com um pixel de 16 bits por vez. Ao fazer cast do buffer para uint32_t*, podemos processar dois pixels por ciclo de CPU.
Intuição:
Precisamos trocar bytes (RGB565 -> BGR565) para dois pixels empacotados em um inteiro de 32 bits 0xAABBCCDD.
- Alvo:
0xBBAADDCC - Máscara 1 (
0xFF00FF00): IsolaAAeCC. - Máscara 2 (
0x00FF00FF): IsolaBBeDD. - Shift e OR: Move-os para as posições corretas simultaneamente.
Implementação (esp32_spi.cpp):
// 1. Cast 16-bit buffer to 32-bit pointer for 2x throughput
uint32_t* buf32 = reinterpret_cast<uint32_t*>(buf);
size_t len32 = len / 2; // Process 2 pixels at once
// 2. Define the SWAR lambda
auto swap_two_pixels = [](uint32_t v) {
// 0xAABBCCDD -> 0xBBAADDCC
return ((v & 0xFF00FF00) >> 8) |
((v & 0x00FF00FF) << 8);
};
// 3. Unrolled loop (8x) to maximize pipeline usage
// This processes 16 pixels per loop iteration!
size_t i = 0;
for (; i < len32 - 8; i += 8) {
buf32[i + 0] = swap_two_pixels(buf32[i + 0]);
buf32[i + 1] = swap_two_pixels(buf32[i + 1]);
buf32[i + 2] = swap_two_pixels(buf32[i + 2]);
buf32[i + 3] = swap_two_pixels(buf32[i + 3]);
buf32[i + 4] = swap_two_pixels(buf32[i + 4]);
buf32[i + 5] = swap_two_pixels(buf32[i + 5]);
buf32[i + 6] = swap_two_pixels(buf32[i + 6]);
buf32[i + 7] = swap_two_pixels(buf32[i + 7]);
}
// 4. Handle remaining pixels
for (; i < len32; i++) {
buf32[i] = swap_two_pixels(buf32[i]);
}
🧠 Análise profunda: Por que usar um buffer partial grande é mais rápido?
Parece contraintuitivo que Buffers Parciais (Fase 5) sejam mais rápidos que Frame Completo (Fase 4).
- Fase 4 (PSRAM):
- Prós: 1x passe de renderização. 0x overhead de tiling.
- Contras: Escritas em PSRAM são ~3-4x mais lentas que em SRAM. A CPU passa um tempo significativo esperando pelo barramento SPI.
- Fase 5 (SRAM Interna):
- Prós: Escritas em SRAM são quase instantâneas (comparativamente).
- Contras: 2x passes de renderização (metade superior, metade inferior).
- Veredito: Para o ESP32-S3 @ 240MHz, o custo de um passe extra de renderização SVG é menor que o custo de escrever 115KB de dados para a PSRAM externa. Esta é uma constatação arquitetural crítica: largura de banda de memória muitas vezes importa mais do que ciclos de computação.
Resultado: ~30 FPS. Animação fluida que atinge os limites do barramento SPI do XIAO Round Display.
🎨 O motor de animação
Nossa UI não exibe apenas imagens estáticas; ela reproduz movimento de alta fidelidade a partir de especificações SVG usando lvgl_cpp. Embora o motor de renderização SVG do LVGL, o ThorVG, não suporte tags de animação em SVGs, este workshop ainda assim utiliza lvgl::Animation para animar os SVGs. Isso é uma utilidade útil para animação e, para o propósito deste workshop, gera carga intensiva de CPU para demonstrar o desempenho das fases do workshop.
A ponte SVG-para-LVGL
Animações SVG frequentemente usam curvas "Cubic Bezier" (definidas como keySplines) para movimento fluido. O LVGL tem um motor Bezier embutido, mas tradicionalmente é usado para easing interno.
Exponibilizamos essa lógica nativamente em lvgl_cpp para que você possa usar pontos de controle SVG padrão diretamente no seu código C++:
// From main/ui/workshop_ui.cpp
// ANIMATION: FLAPPING (SVG keySpline="0.25 0.1 0.25 1.0")
a1.set_var(bird_dsc)
.set_values(0, 7)
.set_duration(150)
// ...
// Explicitly using the SVG cubic bezier curve
.set_path_cb(lvgl::Animation::Path::CubicBezier(256, 102, 256, 1024));
Nota: Os pontos de controle (0.25, 0.1, etc.) são mapeados para o intervalo inteiro 0..1024 usado pelo mecanismo de matemática de ponto fixo do LVGL.
🧪 Mergulho profundo na configuração: sdkconfig.defaults
Para animações de alto desempenho, o seu sdkconfig é tão importante quanto o seu código. Aqui está a análise das configurações usadas neste workshop:
| Config | Valor | Finalidade |
|---|---|---|
CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG | y | Crucial: Transfere o log para o hardware dedicado do S3. Isso libera os GPIO 43/44 no XIAO S3 Plus, que de outra forma são conectados diretamente ao console UART0 e entram em conflito com os pinos de display/toque no Seeed XIAO Round Display. |
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240 | y | Define o clock da CPU para o máximo. Renderização vetorial é matemática pura; cada MHz conta. |
CONFIG_COMPILER_OPTIMIZATION_PERF | y | Habilita otimizações -O3. Isso diz ao compilador para priorizar a velocidade de execução em vez do tamanho do binário (essencial para os loops complexos do ThorVG). |
CONFIG_COMPILER_OPTIMIZATION_LTO | y | Habilita link time optimization. Isso permite que o compilador otimize entre arquivos fonte, potencialmente embutindo seu flush_cb diretamente no loop de renderização do mecanismo. |
CONFIG_SPIRAM | y | Habilita a PSRAM externa de 8MB. Sem isso, você fica limitado a ~320KB de RAM interna, tornando o buffer de quadro completo impossível. |
CONFIG_SPIRAM_MODE_OCT | y | Configura a PSRAM em modo octal (8 linhas de dados). Isso fornece a enorme largura de banda necessária para a CPU ler/gravar quadros de 240x240 pixels sem travamentos. |
CONFIG_LV_USE_THORVG | y | Habilita o mecanismo vetorial C++ de alto desempenho usado pelo LVGL para renderização SVG. |
CONFIG_LV_CACHE_DEF_SIZE | 2097152 | Aloca um cache de imagem de 2MB na PSRAM. Isso basicamente "lembra" quadros renderizados, transformando matemática vetorial cara em simples cópias de memória para quadros estáticos ou repetidos. |
📊 Resumo final de desempenho
| Fase | Otimização | FPS Alvo | Foco principal |
|---|---|---|---|
| Fase 1 | Baseline | ~9 FPS | SPI 20MHz / Atualização Completa |
| Fase 2 | Fundação | ~15 FPS | CPU 240MHz / SPI 80MHz |
| Fase 3 | Paralelismo | ~9 FPS (Regressão!) | Buffer Duplo / DMA |
| Fase 4 | Ajuste Avançado | ~25 FPS | PSRAM / Buffers de Quadro Completo |
| Fase 5 | Nativo | ~30 FPS | SRAM interna / SWAR 32-bit / SIMD |
Nota final
Em um app de produção, você poderia mirar nos parâmetros da Fase 5 desde o primeiro dia. A "jornada" é para ensino, mas o destino é sempre a arquitetura nativa no ESP32-S3. Os arquivos fonte contêm comentários pedagógicos verbosos para explicar por que certos padrões de C++ foram escolhidos.
Suporte Técnico & Discussão de Produto
Obrigado por escolher nossos produtos! Estamos aqui para oferecer diferentes tipos de suporte para garantir que sua experiência com nossos produtos seja a mais tranquila possível. Oferecemos vários canais de comunicação para atender a diferentes preferências e necessidades.