ESP32 2.8黄屏 · 3D旋转演示 + SD卡视频播放
整数定点3D旋转渲染,参考安富莱STM32H7例程(本质是Pawel A. Hernik的开源算法)移植; 点一下屏幕还能切到SD卡JPEG逐帧视频播放。
ESP32-2432S028R (CYD) TFT_eSPI网页烧录已实测 定点数学3D渲染 SPI 80MHz SD卡+TJpg_Decoder 触摸切换整数定点3D旋转渲染,参考安富莱STM32H7例程(本质是Pawel A. Hernik的开源算法)移植; 点一下屏幕还能切到SD卡JPEG逐帧视频播放。
ESP32-2432S028R (CYD) TFT_eSPI网页烧录已实测 定点数学3D渲染 SPI 80MHz SD卡+TJpg_Decoder 触摸切换单立方体 / 十字形复合体 / 花瓣状复合体 / 3x3立方体阵列,每6秒自动切换。
3种像素图案 + 1种星空穿梭 + 1种棋盘格,跟模型一起自动循环。
40MHz起步~28fps,实测提到80MHz后~52fps;测过100MHz发现帧率跟80MHz完全一样,验证了80MHz正好是ESP32 APB总线本身的硬件天花板。
同一份固件加了SD卡JPEG逐帧播放模式,点一下屏幕在3D演示和视频播放之间切换。
核心渲染算法(定点sin/cos查表、无浮点、逐行光栅化、背面剔除、Z排序)来自 Pawel A. Hernik 2019年的开源项目 "3D Filled Vector Graphics"(原本面向Arduino+ST7735小屏), YouTube演示。 参考资料里安富莱STM32H7 800x480裸机版本把这个算法原样保留,只是换了显示输出; 这次移植到本板(320x240)时同样原样保留了顶点/面/配色数据和光栅化算法,只换了 屏幕尺寸和显示驱动(STM32的LTDC+DMA2D换成TFT_eSPI的pushImage)。
屏幕(ST7789,跟同目录03/04工程一致):
| 信号 | GPIO |
|---|---|
| MOSI | IO13 |
| SCLK | IO14 |
| CS | IO15 |
| DC | IO2 |
| BL(背光) | IO21 |
SD卡(走ESP32默认VSPI硬件引脚,跟屏幕是两条独立总线不冲突):
| 信号 | GPIO |
|---|---|
| CS | IO5 |
| SCK | IO18 |
| MISO | IO19 |
| MOSI | IO23 |
SD卡引脚依据是厂商原理图(ESP32-2432S028-MCU.jpg 的SD_Car部分)和厂商自带demo
(6_1_SD_Jpg)两处互相印证,不是网上通用资料猜的。
触摸中断脚: IO36,只用来判断"有没有点一下屏幕"(IRQ被拉低),
不解析XPT2046坐标——点屏幕任意位置都能触发3D/视频模式切换。
| SPI时钟 | 单帧耗时 | 帧率 | 备注 |
|---|---|---|---|
| 40MHz | ~35ms | ~28fps | 沿用同目录其它工程的默认值 |
| 80MHz | ~19ms | ~52fps | 实机长时间点亮确认不花屏 |
| 100MHz | ~19ms | ~52fps | 跟80MHz完全一样,证实80MHz是硬件上限(等于ESP32 APB总线时钟本身) |
瓶颈是SPI往屏幕搬像素数据的带宽,不是ESP32算3D的算力——这份demo每帧要把 320x240整屏像素全部重画(背景铺满+3D图形),跟B站上常见的"STM32F103C8T6+ST7735 实现80帧"对比不公平:那块屏只有128x160(约2万像素),这块屏是320x240(约7.7万像素), 要搬的数据量差了将近4倍。
ESP32算力和内存都不够直接解码标准视频编码(H.264等),做法是先把视频转成 一串JPEG图片(逐帧),存到SD卡,固件按顺序读一张画一张,靠固定的帧间隔造出 "播放视频"的效果,本质是快速幻灯片,不是真正的视频解码器。
Blender基金会官方开放电影《Big Buck Bunny》(CC BY 3.0),剪了40秒高潮片段。
320x160,320张JPEG,共3.9MB,存在SD卡 /bbb/ 目录下(原始素材是180高,sprite内存踩坑后缩到160,见下面"踩坑记录")。
用 TJpg_Decoder 解码进一块320x160的离屏sprite,解完整块一次性推上屏,不会露出解码到一半的中间状态。
点一下屏幕(判断IO36触摸中断脚),在3D演示和视频播放两个模式之间切换。
署名要求:Big Buck Bunny 是 CC BY 3.0 协议,免费能用,但公开发布时按协议要求 署名 "Blender Foundation | bigbuckbunny.org"。
| 问题 | 原因 | 解决 |
|---|---|---|
| 串口上传帧序列,每个文件都缺字节 | ESP32默认串口接收缓冲只有256字节,SD写入阻塞时被写爆丢数据 | 接收端Serial.setRxBufferSize(16384) |
| 切帧时画面闪黑 | 解码块直接画在屏幕上,能看到"旧帧被新帧一块块覆盖"的中间过程 | 先解码进离屏sprite,解完整块一次性pushSprite |
| 加上sprite后视频模式直接黑屏卡死 | 320x180的sprite要115200字节,但堆内存碎片化,最大连续块只有110580字节,createSprite失败 | sprite高度缩到160(102400字节),牺牲一点底部裁切换空间 |
| 播放速度只有~4fps,达不到预期 | SD默认用保守的4MHz初始化 | 提到25MHz,解码耗时226ms降到~140ms(~6-7fps);测过40MHz没有进一步提升,瓶颈变成JPEG解码本身的算力 |
完整版实拍演示+源码解说(11分19秒),前半段实机效果,后半段逐行代码拆开讲:
下面是桌面仿真渲染器跑出来的干净素材(跟固件用同一套定点数学和同一份数据, 只是没有实拍摄像头的反光/摩尔纹),横版1920x1080,覆盖全部4种模型x5种背景组合, 配了一段自己合成的原创8-bit背景音乐(纯波形生成,没有版权问题):
跟实拍对比一下——下面这张是SD卡视频播放模式的实机照片,同样是Big Buck Bunny的画面:
下面是这个项目当前的完整源码,跟仓库里
02_Arduino源码/ESP32_3D_Rotation/src/main.ino 保持同步,可以直接在这页上对着看,
不用另外下载工程。前段的顶点/花纹数据是数值表,篇幅比较长,直接往下滚就能跳过。
/*
ESP32-2432S028R (CYD, 2.8寸黄屏) 整数定点 3D 旋转演示
算法来源:Pawel A. Hernik, "3D Filled Vector Graphics" (2019)
YouTube: https://youtu.be/5y28ipwQs-E
原版面向 Arduino + ST7735,无浮点、无三角函数库,靠查表 sin/cos + 扫描线
光栅化实现平面填色的旋转多面体。
本文件移植自参考资料里安富莱 STM32H7 800x480 裸机版本
(../01_参考资料/基于H7的3D旋转裸机移植(800x480).7z, User/3DTest.c),
顶点/面/配色数据和光栅化算法原样保留,改动的地方:
- 屏幕尺寸从 800x480 换成本板 320x240 (landscape)
- 显示驱动从 STM32 LTDC+DMA2D 换成 TFT_eSPI 的 pushImage 分行刷屏
- setup()/loop() 换成 Arduino 框架写法,用 millis() 做切换计时
- 原来 STM32 例程里没用到的摇杆/按键/串口调试代码删掉了
硬件:ST7789 320x240 (setRotation(1)),接线沿用同目录下其它工程的
标准引脚(见 02_所需库与配置/platformio.ini)。
*/
#include <TFT_eSPI.h>
#include <SD.h>
#include <SPI.h>
#include <TJpg_Decoder.h>
TFT_eSPI tft = TFT_eSPI();
// SD卡(TF卡)接线:这块板子SD走的是ESP32默认VSPI硬件引脚(SCK18/MISO19/MOSI23),
// 跟TFT的自定义HSPI引脚(13/14/15)是两条独立总线,不冲突,SD.begin(SD_CS)不用另外配置SPI。
// 引脚来自厂商原理图 ESP32-2432S028-MCU.jpg 的 SD_Car 部分,跟厂商demo(6_1_SD_Jpg)一致。
#define SD_CS 5
// 触摸中断脚:平时高电平,手指按下会被拉低。这里只用它做"有没有点一下屏幕"的
// 开关判断,不读XPT2046坐标(不需要精确点哪里,只要能切模式)。
#define TOUCH_IRQ 36
// mode: 0=3D旋转演示,1=SD卡视频播放,点一下屏幕切换
int mode = 0;
bool touchWasDown = false;
unsigned long lastToggleMs = 0;
#define VIDEO_FRAME_COUNT 320
#define VIDEO_FPS 8
#define VIDEO_FRAME_H 180
int videoFrameIdx = 1;
unsigned long lastVideoFrameMs = 0;
#define SCR_WD 320
#define SCR_HT 240
#define WD_3D 320
#define HT_3D 240
#define SWAP_INT(a, b) { int t_ = a; a = b; b = t_; }
#define NLINES 40
uint16_t frBuf[SCR_WD*NLINES];
int yFr = 0;
int bgMode = 3;
int object = 1;
int bfCull = 1;
// ------------------------------------------------
#define MAXSIN 255
const uint8_t sinTab[91] = {
0,4,8,13,17,22,26,31,35,39,44,48,53,57,61,65,70,74,78,83,87,91,95,99,103,107,111,115,119,123,
127,131,135,138,142,146,149,153,156,160,163,167,170,173,177,180,183,186,189,192,195,198,200,203,206,208,211,213,216,218,
220,223,225,227,229,231,232,234,236,238,239,241,242,243,245,246,247,248,249,250,251,251,252,253,253,254,254,254,254,254,
255
};
int fastSin(int i)
{
while(i<0) i+=360;
while(i>=360) i-=360;
if(i<90) return((sinTab[i])); else
if(i<180) return((sinTab[180-i])); else
if(i<270) return(-(sinTab[i-180])); else
return(-(sinTab[360-i]));
}
int fastCos(int i)
{
return fastSin(i+90);
}
// ------------------------------------------------
// RGB565,直接对应 TFT_eSPI 的颜色格式(setSwapBytes(true) 已在 setup() 里开启)
#define RGB(r,g,b) (uint16_t)((((r)&0xF8)<<8) | (((g)&0xFC)<<3) | ((b)>>3))
#define CL_BLACK 0x0000
#define COL11 RGB(0,250,250)
#define COL12 RGB(0,180,180)
#define COL13 RGB(0,210,210)
#define COL21 RGB(250,0,250)
#define COL22 RGB(180,0,180)
#define COL23 RGB(210,0,210)
#define COL31 RGB(250,250,0)
#define COL32 RGB(180,180,0)
#define COL33 RGB(210,210,0)
#define COL41 RGB(250,150,0)
#define COL42 RGB(180,100,0)
#define COL43 RGB(210,140,0)
#define COL51 RGB(0,250,0)
#define COL52 RGB(0,180,0)
#define COL53 RGB(0,210,0)
// ------------------------------------------------
/*
* ^Y
* | / Z
* | /
* 7------6
* /| /|
* 3------2 |
* | | | | ----> X
* | 4----|-5
* |/ |/
* 0------1
*
* object 0: 单个立方体
* object 1: 十字形(跟 armfly 例程里 object==1 保持一致,也是默认开机形状)
* object 2: 五角/花瓣状复合立方体
* object 3: 3x3x1 立方体阵列,不做背面剔除
*/
const int16_t numVerts1 = 8;
const int16_t verts1[] = {
-100, -100, -100, // 0
100, -100, -100, // 1
100, 100, -100, // 2
-100, 100, -100, // 3
-100, -100, 100, // 4
100, -100, 100, // 5
100, 100, 100, // 6
-100, 100, 100, // 7
};
const int16_t numQuads1 = 6;
const int16_t quads1[] = {
0, 1, 2, 3, // front
7, 6, 5, 4, // back
4, 5, 1, 0, // bottom
3, 2, 6, 7, // top
7, 4, 0, 3, // left
2, 1, 5, 6, // right
};
const uint16_t quadColor1[] = {
COL33, COL33,COL31,COL31,COL32,COL32,
//RED, GREEN, BLUE, YELLOW, MAGENTA, CYAN
};
const int16_t numVerts2 = 12*2;
const int16_t verts2[] = {
-50, -50, -50, // 0
50, -50, -50, // 1
50, 50, -50, // 2
-50, 50, -50, // 3
-150, 50, -50, // 4
-150, -50, -50, // 5
150, 50, -50, // 6
150, -50, -50, // 7
-50, 150, -50, // 8
50, 150, -50, // 9
-50, -150, -50, // 10
50, -150, -50, // 11
-50, -50, 50, // 0
50, -50, 50, // 1
50, 50, 50, // 2
-50, 50, 50, // 3
-150, 50, 50, // 4
-150, -50, 50, // 5
150, 50, 50, // 6
150, -50, 50, // 7
-50, 150, 50, // 8
50, 150, 50, // 9
-50, -150, 50, // 10
50, -150, 50, // 11
};
const int16_t numQuads2 = 5+5+3*4;
const int16_t quads2[] = {
0, 1, 2, 3, // front mid
7, 6, 2, 1, // front right
4, 5, 0, 3, // front left
3, 2, 9, 8, // front top
10,11,1, 0, // front bottom
12+3, 12+2, 12+1, 12+0, // rear mid
12+1, 12+2, 12+6, 12+7, // rear right
12+3, 12+0, 12+5, 12+4, // rear left
12+8, 12+9, 12+2, 12+3, // rear top
12+0, 12+1,12+11,12+10, // rear bottom
0+12,10+12,10,0,
10+12,11+12,11,10,
11+12,1+12,1,11,
1+12,7+12,7,1,
7+12,6+12,6,7,
6+12,2+12,2,6,
2+12,9+12,9,2,
9+12,8+12,8,9,
8+12,3+12,3,8,
3+12,4+12,4,3,
4+12,5+12,5,4,
5+12,0+12,0,5,
};
const uint16_t quadColor2[] = {
COL31,COL51,COL51,COL51,COL51,
COL31,COL51,COL51,COL51,COL51,
COL52,COL53,COL52,COL53,COL52,COL53,
COL52,COL53,COL52,COL53,COL52,COL53,
};
const int16_t numVerts3 = 8*4;
const int16_t verts3[] = {
-100, -100, -200, // 0 front
100, -100, -200, // 1
100, 100, -200, // 2
-100, 100, -200, // 3
-50, -50, -50, // 4
50, -50, -50, // 5
50, 50, -50, // 6
-50, 50, -50, // 7
-50, -50, 50, // 0 rear
50, -50, 50, // 1
50, 50, 50, // 2
-50, 50, 50, // 3
-100, -100, 200, // 4
100, -100, 200, // 5
100, 100, 200, // 6
-100, 100, 200, // 7
-200, -100, -100, // 0 left
-50, -50, -50, // 1
-50, 50, -50, // 2
-200, 100, -100, // 3
-200, -100, 100, // 4
-50, -50, 50, // 5
-50, 50, 50, // 6
-200, 100, 100, // 7
50, -50, -50, // 0 right
200, -100, -100, // 1
200, 100, -100, // 2
50, 50, -50, // 3
50, -50, 50, // 4
200, -100, 100, // 5
200, 100, 100, // 6
50, 50, 50, // 7
};
const int16_t numQuads3 = 6*4;
const int16_t quads3[] = {
0, 1, 2, 3, // front
7, 6, 5, 4, // back
4, 5, 1, 0, // bottom
3, 2, 6, 7, // top
7, 4, 0, 3, // left
2, 1, 5, 6, // right
8+0, 8+1, 8+2, 8+3, // front
8+7, 8+6, 8+5, 8+4, // back
8+4, 8+5, 8+1, 8+0, // bottom
8+3, 8+2, 8+6, 8+7, // top
8+7, 8+4, 8+0, 8+3, // left
8+2, 8+1, 8+5, 8+6, // right
16+0, 16+1, 16+2, 16+3, // front
16+7, 16+6, 16+5, 16+4, // back
16+4, 16+5, 16+1, 16+0, // bottom
16+3, 16+2, 16+6, 16+7, // top
16+7, 16+4, 16+0, 16+3, // left
16+2, 16+1, 16+5, 16+6, // right
24+0, 24+1, 24+2, 24+3, // front
24+7, 24+6, 24+5, 24+4, // back
24+4, 24+5, 24+1, 24+0, // bottom
24+3, 24+2, 24+6, 24+7, // top
24+7, 24+4, 24+0, 24+3, // left
24+2, 24+1, 24+5, 24+6, // right
};
const uint16_t quadColor3[] = {
COL13,COL13,COL11,COL11,COL12,COL12,
COL23,COL23,COL21,COL21,COL22,COL22,
COL33,COL33,COL31,COL31,COL32,COL32,
COL43,COL43,COL41,COL41,COL42,COL42,
};
const int16_t numVerts4 = 8*9;
const int16_t verts4[] = {
-50, -50, -50, // 0
50, -50, -50, // 1
50, 50, -50, // 2
-50, 50, -50, // 3
-50, -50, 50, // 4
50, -50, 50, // 5
50, 50, 50, // 6
-50, 50, 50, // 7
100-50, 100-50, -50+100, // 0
100+50, 100-50, -50+100, // 1
100+50, 100+50, -50+100, // 2
100-50, 100+50, -50+100, // 3
100-50, 100-50, 50+100, // 4
100+50, 100-50, 50+100, // 5
100+50, 100+50, 50+100, // 6
100-50, 100+50, 50+100, // 7
-100-50, 100-50, -50+100, // 0
-100+50, 100-50, -50+100, // 1
-100+50, 100+50, -50+100, // 2
-100-50, 100+50, -50+100, // 3
-100-50, 100-50, 50+100, // 4
-100+50, 100-50, 50+100, // 5
-100+50, 100+50, 50+100, // 6
-100-50, 100+50, 50+100, // 7
-100-50, -100-50, -50+100, // 0
-100+50, -100-50, -50+100, // 1
-100+50, -100+50, -50+100, // 2
-100-50, -100+50, -50+100, // 3
-100-50, -100-50, 50+100, // 4
-100+50, -100-50, 50+100, // 5
-100+50, -100+50, 50+100, // 6
-100-50, -100+50, 50+100, // 7
100-50, -100-50, -50+100, // 0
100+50, -100-50, -50+100, // 1
100+50, -100+50, -50+100, // 2
100-50, -100+50, -50+100, // 3
100-50, -100-50, 50+100, // 4
100+50, -100-50, 50+100, // 5
100+50, -100+50, 50+100, // 6
100-50, -100+50, 50+100, // 7
100-50, 100-50, -50-100, // 0
100+50, 100-50, -50-100, // 1
100+50, 100+50, -50-100, // 2
100-50, 100+50, -50-100, // 3
100-50, 100-50, 50-100, // 4
100+50, 100-50, 50-100, // 5
100+50, 100+50, 50-100, // 6
100-50, 100+50, 50-100, // 7
-100-50, 100-50, -50-100, // 0
-100+50, 100-50, -50-100, // 1
-100+50, 100+50, -50-100, // 2
-100-50, 100+50, -50-100, // 3
-100-50, 100-50, 50-100, // 4
-100+50, 100-50, 50-100, // 5
-100+50, 100+50, 50-100, // 6
-100-50, 100+50, 50-100, // 7
-100-50, -100-50, -50-100, // 0
-100+50, -100-50, -50-100, // 1
-100+50, -100+50, -50-100, // 2
-100-50, -100+50, -50-100, // 3
-100-50, -100-50, 50-100, // 4
-100+50, -100-50, 50-100, // 5
-100+50, -100+50, 50-100, // 6
-100-50, -100+50, 50-100, // 7
100-50, -100-50, -50-100, // 0
100+50, -100-50, -50-100, // 1
100+50, -100+50, -50-100, // 2
100-50, -100+50, -50-100, // 3
100-50, -100-50, 50-100, // 4
100+50, -100-50, 50-100, // 5
100+50, -100+50, 50-100, // 6
100-50, -100+50, 50-100, // 7
};
const int16_t numQuads4 = 6*9;
const int16_t quads4[] = {
0, 1, 2, 3, // front
7, 6, 5, 4, // back
4, 5, 1, 0, // bottom
3, 2, 6, 7, // top
7, 4, 0, 3, // left
2, 1, 5, 6, // right
8+0, 8+1, 8+2, 8+3, // front
8+7, 8+6, 8+5, 8+4, // back
8+4, 8+5, 8+1, 8+0, // bottom
8+3, 8+2, 8+6, 8+7, // top
8+7, 8+4, 8+0, 8+3, // left
8+2, 8+1, 8+5, 8+6, // right
16+0, 16+1, 16+2, 16+3, // front
16+7, 16+6, 16+5, 16+4, // back
16+4, 16+5, 16+1, 16+0, // bottom
16+3, 16+2, 16+6, 16+7, // top
16+7, 16+4, 16+0, 16+3, // left
16+2, 16+1, 16+5, 16+6, // right
24+0, 24+1, 24+2, 24+3, // front
24+7, 24+6, 24+5, 24+4, // back
24+4, 24+5, 24+1, 24+0, // bottom
24+3, 24+2, 24+6, 24+7, // top
24+7, 24+4, 24+0, 24+3, // left
24+2, 24+1, 24+5, 24+6, // right
32+0, 32+1, 32+2, 32+3, // front
32+7, 32+6, 32+5, 32+4, // back
32+4, 32+5, 32+1, 32+0, // bottom
32+3, 32+2, 32+6, 32+7, // top
32+7, 32+4, 32+0, 32+3, // left
32+2, 32+1, 32+5, 32+6, // right
40+0, 40+1, 40+2, 40+3, // front
40+7, 40+6, 40+5, 40+4, // back
40+4, 40+5, 40+1, 40+0, // bottom
40+3, 40+2, 40+6, 40+7, // top
40+7, 40+4, 40+0, 40+3, // left
40+2, 40+1, 40+5, 40+6, // right
48+0, 48+1, 48+2, 48+3, // front
48+7, 48+6, 48+5, 48+4, // back
48+4, 48+5, 48+1, 48+0, // bottom
48+3, 48+2, 48+6, 48+7, // top
48+7, 48+4, 48+0, 48+3, // left
48+2, 48+1, 48+5, 48+6, // right
56+0, 56+1, 56+2, 56+3, // front
56+7, 56+6, 56+5, 56+4, // back
56+4, 56+5, 56+1, 56+0, // bottom
56+3, 56+2, 56+6, 56+7, // top
56+7, 56+4, 56+0, 56+3, // left
56+2, 56+1, 56+5, 56+6, // right
64+0, 64+1, 64+2, 64+3, // front
64+7, 64+6, 64+5, 64+4, // back
64+4, 64+5, 64+1, 64+0, // bottom
64+3, 64+2, 64+6, 64+7, // top
64+7, 64+4, 64+0, 64+3, // left
64+2, 64+1, 64+5, 64+6, // right
};
const uint16_t quadColor4[] = {
COL13,COL13,COL11,COL11,COL12,COL12,
COL23,COL23,COL21,COL21,COL22,COL22,
COL33,COL33,COL31,COL31,COL32,COL32,
COL43,COL43,COL41,COL41,COL42,COL42,
COL53,COL53,COL51,COL51,COL52,COL52,
COL33,COL33,COL31,COL31,COL32,COL32,
COL43,COL43,COL41,COL41,COL42,COL42,
COL53,COL53,COL51,COL51,COL52,COL52,
COL23,COL23,COL21,COL21,COL22,COL22,
};
const unsigned short pat8[0x0400+3] = {
32,32,16, // width,height,bits
0x1840,0x38c0,0x4100,0x4940,0x4940,0x5161,0x4961,0x2060,0x1860,0x4960,0x4940,0x4940,0x4940,0x4920,0x4940,0x4941,
0x2060,0x4100,0x4920,0x4940,0x4940,0x4940,0x4120,0x1840,0x4960,0x59a1,0x5161,0x4940,0x4940,0x4100,0x4120,0x38e0,
0x5181,0x1020,0x38e0,0x4960,0x5181,0x4961,0x2060,0x4120,0x4120,0x1860,0x5160,0x4940,0x4940,0x4940,0x4920,0x4940,
0x5161,0x2060,0x4100,0x4940,0x4940,0x4940,0x2080,0x4940,0x2080,0x5181,0x59a1,0x5181,0x4960,0x4940,0x4100,0x38e0,
0x5160,0x4961,0x1840,0x4120,0x4961,0x2060,0x4140,0x4961,0x4940,0x30c0,0x1840,0x4940,0x5160,0x4940,0x4920,0x4940,
0x4940,0x4961,0x2080,0x4120,0x4120,0x2060,0x5181,0x5161,0x4120,0x2060,0x5181,0x59a1,0x5161,0x4960,0x4940,0x4100,
0x4920,0x5181,0x4941,0x1840,0x2060,0x4100,0x4941,0x4120,0x38e0,0x4100,0x4100,0x1840,0x4940,0x5181,0x4940,0x4940,
0x4920,0x4941,0x5181,0x1840,0x1840,0x4940,0x5161,0x3900,0x4120,0x4961,0x2080,0x4961,0x5181,0x4941,0x5181,0x5181,
0x59c2,0x4920,0x4120,0x2060,0x3900,0x4140,0x4941,0x30c0,0x3900,0x4120,0x4920,0x1840,0x1840,0x5160,0x5181,0x4940,
0x4940,0x4920,0x4121,0x1840,0x38e0,0x4941,0x4100,0x4120,0x5161,0x5181,0x5161,0x1840,0x4940,0x4940,0x4941,0x59a1,
0x59a1,0x4961,0x1840,0x4120,0x4981,0x4981,0x28a0,0x30c0,0x4140,0x4140,0x1840,0x30c0,0x38e0,0x1840,0x4960,0x5181,
0x4120,0x4100,0x1840,0x4120,0x4940,0x4941,0x5161,0x5161,0x5181,0x5181,0x2080,0x4940,0x1860,0x4120,0x4920,0x5161,
0x38c0,0x2880,0x4920,0x4940,0x51a1,0x28a0,0x30c0,0x4100,0x4120,0x1860,0x4121,0x4121,0x38e0,0x4100,0x1860,0x4120,
0x4120,0x1840,0x4100,0x4120,0x5161,0x5181,0x5181,0x5160,0x51a1,0x2060,0x4100,0x5181,0x4940,0x1860,0x4100,0x4100,
0x1020,0x4120,0x4940,0x5181,0x3900,0x38e0,0x4120,0x30c0,0x1840,0x4120,0x4121,0x4120,0x3900,0x3901,0x4141,0x1840,
0x1840,0x4120,0x4920,0x4941,0x5181,0x5181,0x5160,0x4140,0x2060,0x4140,0x4940,0x4120,0x4961,0x4920,0x2060,0x30a0,
0x1840,0x4120,0x4940,0x4120,0x4100,0x4140,0x4100,0x1840,0x1840,0x4120,0x3900,0x3900,0x38e0,0x3900,0x4981,0x4120,
0x1840,0x4100,0x5161,0x5181,0x5181,0x5180,0x5161,0x1860,0x1840,0x4940,0x4940,0x4120,0x4120,0x4940,0x4920,0x1860,
0x28a0,0x4100,0x4940,0x4120,0x4920,0x3900,0x2060,0x5161,0x4120,0x1840,0x3900,0x38e0,0x30e0,0x38e0,0x4120,0x4961,
0x4100,0x1840,0x4121,0x5161,0x5180,0x4960,0x2060,0x4100,0x4120,0x2060,0x4100,0x4920,0x3900,0x38e0,0x4940,0x4120,
0x5181,0x2060,0x38e0,0x4120,0x4120,0x1860,0x59a1,0x5161,0x4920,0x3900,0x1840,0x4120,0x3920,0x38e0,0x4100,0x4140,
0x4100,0x38c0,0x1840,0x4940,0x5161,0x2060,0x4100,0x4940,0x5160,0x4940,0x2060,0x4120,0x4120,0x30c0,0x38e0,0x4940,
0x4920,0x5181,0x1860,0x3900,0x2060,0x51a1,0x5181,0x4100,0x30c0,0x38e0,0x4120,0x1840,0x4121,0x4120,0x4120,0x4120,
0x4121,0x38e0,0x38e0,0x1840,0x1840,0x4940,0x4920,0x5160,0x59e1,0x5181,0x4120,0x1860,0x3900,0x4940,0x38e0,0x38e0,
0x4940,0x4920,0x4941,0x2060,0x4961,0x5161,0x4100,0x30c0,0x38e0,0x4940,0x4940,0x1840,0x1840,0x4120,0x4940,0x4120,
0x4120,0x4941,0x38c0,0x1840,0x38e0,0x4940,0x5160,0x59c1,0x5181,0x4960,0x4100,0x2060,0x1860,0x4941,0x4940,0x40e0,
0x5161,0x4100,0x2060,0x59a1,0x4960,0x40e0,0x30c0,0x4120,0x4961,0x4940,0x1840,0x4100,0x4120,0x1840,0x4120,0x4940,
0x4120,0x4120,0x1840,0x4940,0x5160,0x5180,0x59c1,0x5181,0x4960,0x4100,0x1840,0x3900,0x38e0,0x1860,0x5161,0x4940,
0x38e0,0x2080,0x51a1,0x5161,0x40e0,0x3900,0x4120,0x5161,0x4961,0x1840,0x4940,0x4940,0x5181,0x4940,0x1860,0x4120,
0x4120,0x1840,0x4920,0x5181,0x4940,0x59c1,0x5181,0x5181,0x4120,0x1840,0x4120,0x38e0,0x4100,0x3900,0x2060,0x5181,
0x1840,0x5181,0x5181,0x4100,0x4100,0x4940,0x4960,0x3900,0x1840,0x4940,0x5161,0x4920,0x4960,0x5160,0x4940,0x2060,
0x1860,0x4940,0x5160,0x4960,0x4920,0x5181,0x5181,0x38e0,0x1860,0x4120,0x4940,0x4120,0x4120,0x5161,0x4960,0x1840,
0x1860,0x4940,0x4920,0x4920,0x4940,0x4940,0x4941,0x1840,0x1820,0x4100,0x5160,0x4940,0x4920,0x5160,0x5160,0x4941,
0x2060,0x4120,0x5160,0x4920,0x5160,0x5160,0x4920,0x1840,0x1840,0x4941,0x4940,0x4940,0x5160,0x5181,0x59a1,0x4140,
0x4941,0x1840,0x4120,0x4960,0x5161,0x4941,0x1840,0x4120,0x3900,0x1840,0x4100,0x38e0,0x38e0,0x4940,0x5160,0x4140,
0x4121,0x2060,0x4120,0x4920,0x5160,0x4100,0x1840,0x4120,0x4120,0x1840,0x4941,0x5161,0x5181,0x61e1,0x5181,0x59a1,
0x4100,0x4941,0x1840,0x4120,0x4940,0x1840,0x4960,0x51a1,0x4961,0x4120,0x1020,0x4120,0x30e0,0x38e0,0x4960,0x4940,
0x38e0,0x4941,0x2060,0x4120,0x4920,0x1840,0x3900,0x4120,0x4941,0x4121,0x1840,0x4940,0x4941,0x5181,0x6222,0x5161,
0x4100,0x4100,0x4941,0x1840,0x1040,0x4960,0x51a1,0x4120,0x4120,0x38e0,0x3900,0x1840,0x4100,0x38e0,0x4100,0x4940,
0x4100,0x38e0,0x4120,0x1840,0x1820,0x3900,0x4100,0x4941,0x4121,0x4120,0x3900,0x1840,0x4100,0x4941,0x59c1,0x59c1,
0x59a1,0x4100,0x3900,0x1840,0x4100,0x59c1,0x4100,0x3900,0x30c0,0x4100,0x4940,0x1840,0x1840,0x4940,0x38e0,0x4920,
0x4940,0x4120,0x4120,0x1840,0x30c0,0x4120,0x4961,0x4141,0x4120,0x4120,0x3900,0x1840,0x1840,0x5161,0x4921,0x5181,
0x61e2,0x4141,0x1840,0x4140,0x59c1,0x4120,0x30e0,0x30c0,0x38e0,0x4120,0x2060,0x4120,0x4100,0x2060,0x4120,0x38e0,
0x4940,0x4100,0x1840,0x38e0,0x3900,0x4961,0x4121,0x38e0,0x3900,0x38e0,0x1860,0x4941,0x38e0,0x1860,0x5161,0x4921,
0x4941,0x2880,0x38e0,0x4941,0x4121,0x30c0,0x28a0,0x30c0,0x4120,0x1860,0x5181,0x5181,0x4940,0x4940,0x2060,0x4121,
0x3900,0x1840,0x3900,0x38e0,0x4160,0x3900,0x30c0,0x38e0,0x38e0,0x1040,0x4940,0x59c1,0x5181,0x38e0,0x2060,0x5161,
0x1020,0x4100,0x4941,0x4121,0x30c0,0x2080,0x30e0,0x30e0,0x1840,0x4941,0x4940,0x5161,0x5181,0x4960,0x4940,0x2060,
0x2060,0x4120,0x3900,0x3900,0x30e0,0x28a0,0x30c0,0x30c0,0x1040,0x4120,0x4940,0x61e1,0x59c1,0x5181,0x4120,0x1860,
0x1020,0x4100,0x4941,0x38e0,0x30c0,0x4100,0x4940,0x1860,0x1840,0x4961,0x5181,0x51a1,0x5181,0x5181,0x4940,0x4120,
0x2080,0x3900,0x4120,0x3900,0x30c0,0x30c0,0x38e0,0x1840,0x1840,0x5161,0x5160,0x4940,0x6201,0x59a1,0x5161,0x4100,
0x38c0,0x1020,0x3900,0x4120,0x4120,0x4940,0x2060,0x4941,0x4120,0x1840,0x51a1,0x6202,0x5181,0x5181,0x5181,0x4960,
0x4940,0x2080,0x3920,0x38e0,0x38e0,0x4121,0x1840,0x4141,0x4120,0x2060,0x5161,0x4940,0x51a1,0x61e1,0x5181,0x5161,
0x4940,0x38e0,0x1020,0x4120,0x4940,0x2060,0x4941,0x5161,0x4920,0x30c0,0x1860,0x59a2,0x61e1,0x5161,0x5181,0x5181,
0x4940,0x4940,0x2080,0x3900,0x4941,0x1840,0x51a1,0x61e2,0x4961,0x4121,0x2060,0x4940,0x5160,0x59c1,0x5181,0x4961,
0x51a1,0x4940,0x3900,0x1020,0x2060,0x4940,0x5161,0x4940,0x4100,0x4920,0x4961,0x2060,0x51a1,0x61e2,0x4940,0x5161,
0x5161,0x4940,0x4120,0x1860,0x1860,0x5181,0x6222,0x51a1,0x4961,0x5161,0x4120,0x1860,0x3900,0x5181,0x59a1,0x5181,
0x59c1,0x5181,0x3900,0x2060,0x4120,0x4960,0x4920,0x4100,0x4940,0x59a1,0x59c1,0x1840,0x1860,0x59e2,0x59a1,0x4920,
0x5181,0x5181,0x4120,0x1840,0x4961,0x6201,0x59c1,0x5181,0x5161,0x4940,0x4120,0x1840,0x1840,0x4921,0x59a1,0x61e2,
0x5181,0x4961,0x2060,0x4940,0x5160,0x4920,0x4100,0x4940,0x59a1,0x51a1,0x2060,0x4120,0x38e0,0x2060,0x59c2,0x4960,
0x4940,0x4961,0x2080,0x4120,0x59a1,0x59a1,0x5181,0x5161,0x4941,0x38e0,0x1840,0x4120,0x3900,0x1840,0x4920,0x59a1,
0x38e0,0x2080,0x4940,0x5160,0x4940,0x4100,0x4940,0x59a1,0x51a1,0x2060,0x4940,0x59a1,0x4940,0x3900,0x2060,0x51a1,
0x4120,0x2060,0x4940,0x4940,0x59a1,0x5181,0x5160,0x4940,0x4100,0x1840,0x38e0,0x4940,0x5160,0x38e0,0x1840,0x4100,
0x1840,0x4920,0x4940,0x4940,0x4940,0x4940,0x59a1,0x4141,0x1840,0x4120,0x5160,0x5181,0x5160,0x4960,0x3900,0x2060,
0x2880,0x4940,0x4960,0x5160,0x5181,0x5160,0x4960,0x38e0,0x1860,0x4941,0x4940,0x4940,0x4940,0x4940,0x38c0,0x1020,
};
const unsigned short pat7[0x0400+3] = {
32,32,16, // width,height,bits
0x8b86,0x8b86,0x4180,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x4180,0x5a84,0x5a84,0x5a84,0x5a84,0x5a84,
0x5a84,0x41e1,0x41e1,0x41e1,0x41e1,0x5a21,0x5a21,0x5a21,0x41e1,0x41e1,0x4180,0xa448,0xa448,0xa448,0xa448,0xa448,
0x8b86,0x8b86,0x4180,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x4180,0x7346,0x7346,0x7346,0x7346,0x7346,
0x5a84,0x41e1,0xa448,0xa448,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x4180,0xa448,0x8b86,0x8b86,0x8b86,0x8b86,
0x8b86,0x8b86,0x4180,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x41e1,0x4180,0x7346,0x7346,0x7346,0x7346,0x7346,
0x5a84,0x41e1,0xa448,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x4180,0xa448,0xa446,0x8b86,0x8b86,0x8b86,
0x4180,0x4180,0x4180,0x4180,0x5a21,0x5a21,0x4180,0x5a21,0x5a21,0x5a21,0x4180,0x5a84,0x5a84,0x5a84,0x5a84,0x5a84,
0x5a84,0x4180,0x4180,0x4180,0x5a21,0x7263,0x7263,0x7263,0x7263,0x4180,0x4180,0x4180,0x5a21,0x5a21,0x4180,0x4180,
0x8b84,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x41e1,0x7263,0x5a21,0x5a21,0x5a21,0x5a21,0x5a21,0x7263,
0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x5a21,0x8b84,0x8b84,0x8b84,0x8b84,0x8b84,0x8b84,0x8b84,
0x8b84,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x41e1,0x7263,0x5a21,0x5a21,0x5a21,0x5a21,0x5a21,0x7263,
0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x4180,0xa446,0x8b84,0x8b84,0x8b84,0x8b84,0x8b84,0x8b84,
0x8b84,0x8b84,0x8b84,0x7263,0x7263,0x7263,0x7263,0x7263,0x41e1,0x7263,0x5a21,0x5a21,0x5a21,0x5a21,0x5a21,0x7263,
0x8b84,0x8b84,0x8b84,0x7263,0x7263,0x7263,0x7263,0x7263,0x4180,0xa446,0xa446,0xa446,0x8b84,0x8b84,0x8b84,0x8b84,
0x4180,0x4180,0x5a21,0x5a21,0x5a21,0x7263,0x5a21,0x5a21,0x41e1,0x7263,0x7263,0x5a21,0x41e1,0x7263,0x7263,0x7263,
0x7263,0x41e1,0x41e1,0x41e1,0x7263,0x7263,0x5a21,0x5a21,0x5a21,0x41e1,0x41e1,0x41e1,0x5a21,0x5a21,0x5a21,0x5a21,
0x7263,0x7263,0x5a21,0xa448,0xa448,0xa448,0x8b86,0x5a21,0x7263,0x7263,0x7263,0x7263,0x41e1,0x7263,0x7263,0x7263,
0x7263,0x7263,0x41e1,0xa446,0xa446,0x8b84,0x8b84,0x5a21,0x7263,0x7263,0x7263,0x41e1,0x7263,0x7263,0x7263,0x7263,
0x7263,0x7263,0x5a21,0xa448,0xa446,0x8b86,0x8b86,0x4180,0x7263,0x7263,0x7263,0x7263,0x5a21,0x7263,0x7263,0x7263,
0x7263,0x7263,0x5a21,0xa446,0xa446,0x8b84,0x8b84,0x4180,0x7263,0x7263,0x7263,0x5a21,0x7263,0x7263,0x7263,0x7263,
0x7263,0x7263,0x5a21,0xa448,0x8b86,0x8b86,0x8b86,0x4180,0x8b84,0x7263,0x7263,0x7263,0x5a21,0x7263,0x7263,0x7263,
0x7263,0x7263,0x5a21,0xa446,0xa446,0xa446,0x8b84,0x4180,0x8b84,0x8b84,0x8b84,0x8b84,0x7263,0x7263,0x7263,0x7263,
0x7263,0x7263,0x7263,0x7263,0x7263,0x41e1,0x7263,0x7263,0x7263,0x7263,0x7263,0x5a21,0x8b84,0x8b84,0x8b84,0x8b84,
0x8b84,0x8b84,0x5a21,0x5a21,0x5a21,0x41e1,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x4180,0x4180,0x4180,0x4180,
0x7263,0x7263,0x7263,0x7263,0x7263,0x41e1,0x7263,0x7263,0x7263,0x7263,0x7263,0x5a21,0x8b84,0x8b84,0x8b84,0x8b84,
0x8b84,0x8b84,0x5a21,0x5a21,0x5a21,0x41e1,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x4180,
0x7263,0x7263,0x7263,0x7263,0x7263,0x41e1,0x5a21,0x8b84,0x8b84,0x8b84,0x7263,0x5a21,0x8b84,0x8b84,0x8b84,0x8b84,
0x8b84,0x8b84,0x5a21,0x5a21,0x5a21,0x41e1,0x5a21,0x8b84,0x8b84,0x8b84,0x7263,0x7263,0x7263,0x7263,0x5a21,0x4180,
0x5a21,0x4180,0x4180,0x4180,0x5a21,0x41e1,0x4180,0x5a21,0x5a21,0x5a21,0x4180,0x4180,0x5a21,0x5a21,0x4180,0x4180,
0x5a21,0x4180,0x5a21,0x4180,0x5a21,0x41e1,0x4180,0x5a21,0x5a21,0x5a21,0x4180,0x4180,0x5a21,0x5a21,0x4180,0x4180,
0x8b84,0x7263,0x4180,0x8b86,0xa448,0x8b86,0x8b86,0x8b86,0x8b84,0x4180,0x8b84,0xa446,0x8b84,0x8b84,0x7263,0x7263,
0x7263,0x7263,0x4180,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x4180,0xa446,0x8b84,0x8b84,0x8b84,0x8b84,
0x8b84,0x7263,0x4180,0x8b86,0x8b86,0x8b86,0x8b84,0x8b84,0x8b84,0x4180,0x8b84,0xa446,0xa446,0x8b84,0x7263,0x7263,
0x7263,0x7263,0x4180,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x4180,0xa446,0xa446,0x8b84,0x8b84,0x8b84,
0x8b84,0x7263,0x4180,0x8b86,0x8b86,0x8b84,0x8b84,0x8b84,0x8b84,0x4180,0x8b84,0x8b84,0x8b84,0x7263,0x7263,0x7263,
0x7263,0x7263,0x4180,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x4180,0xa446,0xa446,0x8b84,0x8b84,0x8b84,
0x5a21,0x4180,0x4180,0x4180,0x5a21,0x5a21,0x4180,0x7263,0x7263,0x7263,0x7263,0x7263,0x5a21,0x5a21,0x7263,0x4180,
0x4180,0x4180,0x4180,0x4180,0x5a21,0x5a21,0x4180,0x4180,0x41e1,0x41e1,0x41e1,0x4180,0x5a21,0x5a21,0x5a21,0x4180,
0x7346,0x7346,0x7346,0x7346,0x7346,0x7346,0x7346,0x7346,0x5a21,0x8b84,0x8b84,0x8b84,0x8b84,0x8b84,0x8b84,0x8b84,
0x7263,0xa446,0xa446,0xa446,0xa446,0xa446,0x8b84,0x8b84,0x41e1,0x8b84,0x8b84,0x8b84,0x8b84,0x8b84,0x7263,0x4180,
0x7346,0x7346,0x7346,0x7346,0x7346,0x7346,0x7346,0x7346,0x4180,0xa446,0x8b84,0x8b84,0x8b84,0x8b84,0x8b84,0x7263,
0x7263,0xbce6,0xa446,0xa446,0xa446,0xa446,0x8b84,0x8b84,0x7263,0xa446,0x8b84,0x8b84,0x8b84,0x8b84,0x7263,0x4180,
0x7346,0x7346,0x7346,0x7346,0x7346,0x7346,0x7346,0x7346,0x4180,0xa446,0xa446,0xa446,0x8b84,0x8b84,0x8b84,0x7263,
0x7263,0x8b84,0x8b84,0x8b84,0x8b84,0x8b84,0x8b84,0x8b84,0x41e1,0xa446,0xa446,0xa446,0x8b84,0x8b84,0x7263,0x4180,
0x4180,0x4180,0x4180,0x4180,0x5a21,0x5a21,0x5a21,0x5a21,0x5a21,0x5a21,0x7263,0x41e1,0x41e1,0x41e1,0x7263,0x7263,
0x5a21,0x5a21,0x41e1,0x5a21,0x5a21,0x5a21,0x41e1,0x41e1,0x41e1,0x41e1,0x7263,0x5a21,0x5a21,0x5a21,0x5a21,0x4180,
0x7263,0x7263,0x4180,0xa446,0xa446,0x8b84,0x8b84,0x5a21,0x7263,0x7263,0x7263,0x7263,0x41e1,0x8b84,0x8b84,0x8b84,
0x8b84,0x7263,0x41e1,0xa448,0xa448,0xa448,0x8b86,0x5a21,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,
0x7263,0x7263,0x4180,0xa446,0xa446,0x8b84,0x8b84,0x41e1,0x7263,0x7263,0x7263,0x7263,0x41e1,0x8b84,0x8b84,0x8b84,
0x8b84,0x7263,0x5a21,0xa448,0xa448,0xa448,0x8b86,0x4180,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,
0x7263,0x7263,0x5a21,0xa446,0xa446,0xa446,0x8b84,0x41e1,0x8b84,0x7263,0x7263,0x7263,0x41e1,0x8b84,0x8b84,0x8b84,
0x8b84,0x7263,0x5a21,0x8b86,0x8b86,0x8b86,0x8b86,0x4180,0x8b84,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,0x7263,
0x4180,0x4180,0x4180,0x5a21,0x5a21,0x5a21,0x5a21,0x41e1,0x41e1,0x41e1,0x41e1,0x41e1,0x41e1,0x7263,0x7263,0x41e1,
0x4180,0x41e1,0x41e1,0x41e1,0x41e1,0x41e1,0x41e1,0x41e1,0x41e1,0x41e1,0x41e1,0x5a21,0x41e1,0x4180,0x4180,0x4180,
0x5a21,0x5a21,0x5a21,0x5a21,0x5a21,0x41e1,0xa448,0xa448,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x41e1,
0x7263,0x7263,0x7263,0x4180,0x8b84,0x7346,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x4180,0x5a21,0x5a21,
0x7263,0x7263,0x7263,0x7263,0x7263,0x41e1,0xa448,0xa448,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x7346,0x7346,0x41e1,
0x8b84,0x8b84,0x8b84,0x4180,0x8b84,0x8b84,0x7346,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x4180,0x7263,0x5a21,
0x7263,0x7263,0x7263,0x7263,0x7263,0x41e1,0xa448,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x7346,0x5a21,0x4180,
0x8b84,0x8b84,0x8b84,0x4180,0x8b84,0x8b84,0x8b84,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x4180,0x7263,0x5a21,
0x7263,0x7263,0x7263,0x7263,0x7263,0x41e1,0xa448,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x7346,0x5a21,0x4180,
0x8b84,0x8b84,0x8b84,0x4180,0x8b84,0x8b84,0x8b84,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x8b86,0x4180,0x7263,0x5a21,
0x5a21,0x4180,0x4180,0x4180,0x5a21,0x5a21,0x5a84,0x5a84,0x5a84,0x5a84,0x5a84,0x5a84,0x5a84,0x5a84,0x7263,0x4180,
0x7263,0x7263,0x7263,0x7263,0x7263,0x3140,0x7263,0x7263,0x7263,0x7263,0x7263,0x4180,0x5a21,0x5a21,0x4180,0x4180,
};
const unsigned short pat2[0x0400+3] = {
32,32,16, // width,height,bits
0x3829,0x1804,0x3809,0x500d,0x500d,0x500d,0x500d,0x480c,0x480c,0x500d,0x500d,0x584e,0x70d1,0x3809,0x1003,0x1803,
0x3809,0x400b,0x480b,0x500d,0x70f2,0x7913,0x8113,0x70b2,0x402b,0x1804,0x3007,0x500d,0x500d,0x500d,0x500d,0x8173,
0x508c,0x1804,0x3809,0x500e,0x500d,0x606f,0x608f,0x606e,0x502d,0x480c,0x500d,0x8133,0x68b0,0x2005,0x1003,0x1803,
0x1804,0x2005,0x2006,0x3008,0x588d,0x7111,0x70d1,0x482b,0x2005,0x3008,0x480c,0x582e,0x502d,0x584d,0x582e,0x70f2,
0x6930,0x1804,0x3008,0x602f,0x604f,0x500d,0x500d,0x500d,0x500d,0x582e,0x8954,0x70d2,0x2807,0x1804,0x2807,0x586e,
0x70f1,0x68f0,0x486b,0x2006,0x1804,0x2005,0x2806,0x2005,0x3809,0x500d,0x580e,0x502d,0x584d,0x584d,0x502d,0x604f,
0x81b3,0x1805,0x2807,0x582e,0x8934,0x8173,0x8174,0x8153,0x78d2,0x8955,0x78f2,0x2806,0x1805,0x3829,0x8153,0x9997,
0x9156,0x9155,0x99b6,0x8975,0x508d,0x1804,0x0801,0x1804,0x3007,0x400b,0x6870,0x6850,0x480c,0x482c,0x482c,0x606f,
0x8153,0x1805,0x1805,0x400a,0x602f,0x70b2,0x8935,0x9156,0x8935,0x6890,0x2806,0x2005,0x506d,0xa1f7,0xa218,0x78d3,
0x500e,0x500d,0x580e,0x70b1,0x9175,0x8133,0x508c,0x1804,0x1804,0x2806,0x3829,0x78f2,0x6050,0x480c,0x480c,0x606f,
0x586e,0x2005,0x1002,0x1804,0x2006,0x3008,0x3809,0x3809,0x3809,0x2005,0x2005,0x60ae,0xaa78,0x99d7,0x78f2,0x604f,
0x500d,0x500d,0x500d,0x500d,0x6870,0x7091,0x9155,0x7932,0x3809,0x1804,0x1804,0x4069,0x8974,0x78f2,0x604f,0x8133,
0x2807,0x1804,0x1805,0x2006,0x2006,0x2806,0x2806,0x1804,0x0802,0x1804,0x608f,0xaa79,0x99f6,0x7913,0x6871,0x500e,
0x500d,0x500d,0x500d,0x582e,0x7953,0x91f5,0x99f6,0x9196,0x8134,0x484b,0x1804,0x1803,0x486b,0x70d1,0x70d1,0x60ce,
0x1804,0x2806,0x50ad,0x7131,0x7972,0x7952,0x506c,0x3007,0x1804,0x402a,0x99d7,0x91f6,0x91f6,0x6891,0x600f,0x580e,
0x500d,0x500d,0x500d,0x500d,0x500d,0x68b0,0x7952,0x8153,0x91b5,0x91b5,0x502c,0x1804,0x2005,0x380a,0x400a,0x2806,
0x3829,0x502d,0x6870,0x78d2,0x91d6,0xaa78,0x9a17,0x406a,0x1804,0x70d1,0x99d7,0x8154,0x8974,0x6890,0x580e,0x500d,
0x500d,0x582e,0x582e,0x582e,0x582e,0x582e,0x500d,0x582e,0x6870,0x89b5,0x8113,0x3809,0x1003,0x1003,0x1804,0x2005,
0x6870,0x580e,0x500d,0x582e,0x70d1,0x99d6,0xaa58,0x48cb,0x2006,0x8955,0x9175,0x7913,0x7912,0x68b0,0x580e,0x500d,
0x480b,0x482c,0x482c,0x502d,0x500d,0x500d,0x500d,0x500d,0x604f,0x8133,0x8974,0x482c,0x1804,0x1805,0x486b,0x7131,
0x500d,0x500d,0x500d,0x580e,0x70b1,0x91d6,0xaa58,0x40aa,0x3008,0x99d6,0x8955,0x70b1,0x70d1,0x6090,0x500d,0x580e,
0x480c,0x480b,0x480c,0x480c,0x480c,0x500d,0x480c,0x480c,0x7111,0x7932,0x91f6,0x604f,0x2006,0x3808,0x7912,0x604f,
0x500d,0x502d,0x502d,0x582e,0x608f,0x7912,0xa258,0x3849,0x3008,0x99f6,0x8975,0x6891,0x606f,0x500d,0x500d,0x500d,
0x500c,0x584e,0x584e,0x584d,0x502d,0x500c,0x480c,0x502c,0x68d0,0x68b0,0x91d5,0x586e,0x2006,0x4029,0x608f,0x500d,
0x500d,0x500c,0x502d,0x586e,0x582e,0x6890,0x8995,0x3829,0x2807,0x8173,0x9195,0x7913,0x6870,0x480c,0x500d,0x500d,
0x502d,0x504d,0x584d,0x584e,0x482c,0x502d,0x502d,0x502d,0x500c,0x584e,0x7912,0x482c,0x1804,0x50cd,0x606f,0x500d,
0x500d,0x500d,0x582e,0x502d,0x580e,0x7091,0x8154,0x3008,0x2006,0x7932,0x8133,0x8995,0x6870,0x500c,0x500c,0x502d,
0x586e,0x586e,0x482c,0x400b,0x400b,0x480b,0x480c,0x500d,0x500d,0x500d,0x584f,0x3809,0x2006,0x60ef,0x580e,0x500d,
0x500d,0x500d,0x500d,0x500d,0x500d,0x606f,0x584d,0x2005,0x1804,0x70f2,0x70d2,0x68b1,0x6850,0x500d,0x480c,0x480c,
0x584e,0x586e,0x502d,0x480c,0x480c,0x500d,0x500d,0x500d,0x500d,0x500d,0x500d,0x2806,0x3048,0x7111,0x500d,0x500d,
0x500d,0x500d,0x500d,0x500c,0x480c,0x400a,0x2807,0x1003,0x1003,0x586e,0x8134,0x70d1,0x6891,0x580f,0x500d,0x500c,
0x480c,0x480c,0x502d,0x502d,0x502d,0x500d,0x500d,0x500d,0x500d,0x500d,0x400b,0x1804,0x3808,0x68b0,0x500d,0x500d,
0x400b,0x400a,0x3008,0x2807,0x2806,0x2005,0x1804,0x1804,0x1002,0x402a,0x9156,0x70b1,0x68d1,0x6030,0x500d,0x480b,
0x400b,0x400b,0x400b,0x400b,0x480c,0x480c,0x480c,0x480c,0x580e,0x500d,0x2807,0x0802,0x2005,0x3809,0x480b,0x480c,
0x2006,0x2005,0x1804,0x2005,0x3809,0x480c,0x586e,0x480b,0x2005,0x2005,0x70d1,0x8114,0x584e,0x604f,0x580f,0x500d,
0x500d,0x480c,0x480b,0x480b,0x500d,0x500d,0x582e,0x78f2,0x582e,0x3008,0x1003,0x1003,0x1804,0x2005,0x2005,0x2806,
0x3849,0x3809,0x402b,0x68f0,0x89b5,0x9a17,0xa257,0x91f6,0x484b,0x1804,0x3809,0x8134,0x7092,0x480c,0x500d,0x500d,
0x500d,0x500d,0x500d,0x500d,0x500d,0x70f1,0x91d6,0x68d0,0x2807,0x1804,0x3008,0x606f,0x70f1,0x58ae,0x508c,0x404a,
0x8974,0x8154,0x9195,0xa237,0x99f6,0x91b5,0x99d6,0xb2b9,0x9215,0x3829,0x1804,0x484b,0x80f4,0x7091,0x502d,0x580e,
0x582f,0x500d,0x580e,0x6030,0x8154,0x91d5,0x508d,0x2005,0x1002,0x3809,0x70b1,0x9a17,0x8134,0x8113,0x99f6,0x91b6,
0x582e,0x604f,0x7112,0x91b5,0x7913,0x70d2,0x8974,0xaa98,0xa217,0x81b3,0x2806,0x2005,0x586e,0x9176,0x8994,0x584f,
0x600f,0x6030,0x602f,0x8954,0x91b5,0x488b,0x1003,0x0802,0x1002,0x400b,0x99f7,0x8994,0x580e,0x582e,0x604f,0x602f,
0x500d,0x586e,0x608f,0x68d0,0x68d0,0x68b1,0x606f,0x9a16,0x91d6,0x99f7,0x6950,0x1805,0x2807,0x584e,0x8114,0x91b5,
0x7952,0x8133,0x91b5,0x7111,0x3028,0x1804,0x1805,0x2005,0x1003,0x3008,0x91f6,0x70b1,0x602f,0x604f,0x582e,0x500d,
0x500d,0x500d,0x500d,0x502d,0x68b0,0x502d,0x480c,0x70f1,0x8193,0x7913,0x8994,0x3849,0x1003,0x2006,0x482c,0x68d0,
0x7912,0x70d1,0x506c,0x2806,0x2806,0x3809,0x502c,0x400a,0x2806,0x1805,0x7131,0x70b1,0x604f,0x584f,0x582e,0x500d,
0x500d,0x500d,0x500d,0x500c,0x480c,0x480c,0x480c,0x584d,0x7972,0x68d0,0x9195,0x7932,0x2005,0x1002,0x1003,0x1804,
0x2005,0x1805,0x1804,0x3808,0x608f,0x78f3,0x91b6,0x8153,0x3809,0x2005,0x406b,0x606f,0x580e,0x604f,0x584e,0x500d,
0x500d,0x500d,0x500d,0x500d,0x582e,0x582e,0x500d,0x500d,0x70f1,0x68d0,0x7912,0x99b6,0x506d,0x1804,0x0802,0x1805,
0x2806,0x3008,0x60ae,0x8994,0x89b4,0x8193,0x6890,0x99f6,0x60af,0x2006,0x2806,0x480b,0x500d,0x580e,0x582e,0x500d,
0x500d,0x500d,0x500d,0x502d,0x586e,0x606f,0x606f,0x606f,0x584e,0x582e,0x586e,0x8954,0x8113,0x3008,0x1003,0x3007,
0x68b0,0x8133,0x70f1,0x586e,0x7111,0x68f0,0x500d,0x8153,0x8954,0x3008,0x1804,0x2006,0x3008,0x480b,0x500d,0x500d,
0x500d,0x500d,0x500c,0x480c,0x502d,0x584e,0x502d,0x502d,0x480c,0x480b,0x504d,0x70d1,0x8934,0x504d,0x2005,0x3008,
0x91b5,0x6870,0x480c,0x480c,0x502d,0x480c,0x480c,0x586e,0x7953,0x3829,0x1003,0x1003,0x1804,0x2005,0x400a,0x500d,
0x500d,0x500d,0x480c,0x480b,0x400b,0x482c,0x584d,0x480c,0x480c,0x500d,0x582e,0x608f,0x8113,0x68f0,0x2005,0x3027,
0x8193,0x502d,0x480b,0x480c,0x584e,0x582e,0x582e,0x500d,0x7912,0x484b,0x1003,0x508c,0x58ae,0x3007,0x2005,0x3809,
0x400a,0x500d,0x500d,0x500d,0x480c,0x480b,0x480c,0x480c,0x480c,0x500c,0x582e,0x60af,0x70d1,0x7111,0x2807,0x2806,
0x70f1,0x502d,0x480c,0x480c,0x504d,0x502d,0x500d,0x500d,0x70d1,0x484c,0x1804,0x58ad,0x6890,0x480c,0x3008,0x2005,
0x2006,0x480c,0x500d,0x500d,0x500d,0x480c,0x480c,0x480c,0x480c,0x480c,0x584e,0x60af,0x604f,0x70f1,0x3007,0x2806,
0x582f,0x500d,0x502e,0x584e,0x502d,0x480c,0x480c,0x480c,0x70b1,0x506d,0x2005,0x400b,0x500d,0x500d,0x400b,0x3007,
0x2006,0x3007,0x500d,0x500d,0x500d,0x500d,0x500d,0x500d,0x500d,0x500d,0x606f,0x584e,0x582e,0x68b0,0x3007,0x2806,
0x500d,0x500d,0x500d,0x500d,0x500d,0x500d,0x500d,0x500d,0x6871,0x482b,0x2005,0x400b,0x500d,0x500d,0x68d0,0x402a,
0x3007,0x2006,0x480b,0x500d,0x500d,0x500d,0x500d,0x500d,0x500d,0x586e,0x7111,0x500d,0x582e,0x586d,0x2005,0x2006,
0x500d,0x500d,0x500d,0x500d,0x582e,0x580e,0x500d,0x602f,0x6871,0x3007,0x2006,0x400b,0x500d,0x500d,0x6890,0x6930,
};
// -----------------------------------------------
int16_t numVerts = numVerts1;
int16_t *verts = (int16_t*)verts1;
int16_t numQuads = numQuads1;
int16_t *quads = (int16_t*)quads1;
uint16_t *quadColor = (uint16_t*)quadColor1;
#define MAXQUADS 6*20
#define MAXVERTS 8*20
int transVerts[MAXVERTS*3];
int projVerts[MAXVERTS*2];
int sortedQuads[MAXQUADS];
int rot0 = 0, rot1 = 0;
int numVisible = 0;
// simple Amiga-like blitter implementation
void rasterize(int x0, int y0, int x1, int y1, int *line)
{
if((y0<yFr && y1<yFr) || (y0>=yFr+NLINES && y1>=yFr+NLINES)) return; // exit if line outside rasterized area
int dx = abs(x1 - x0);
int dy = abs(y1 - y0);
int err2,err = dx-dy;
int sx = (x0 < x1) ? 1 : -1;
int sy = (y0 < y1) ? 1 : -1;
while(1) {
if(y0>=yFr && y0<yFr+NLINES) {
if(x0<line[2*(y0-yFr)+0]) line[2*(y0-yFr)+0] = x0>0 ? x0 : 0;
if(x0>line[2*(y0-yFr)+1]) line[2*(y0-yFr)+1] = x0<WD_3D ? x0 : WD_3D-1;
}
if(x0==x1 && y0==y1) return;
err2 = err+err;
if(err2 > -dy) { err -= dy; x0 += sx; }
if(err2 < dx) { err += dx; y0 += sy; }
}
}
void drawQuad( int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3, uint16_t c)
{
int x,y;
int line[NLINES*2];
for(y=0;y<NLINES;y++) { line[2*y+0] = WD_3D+1; line[2*y+1] = -1; }
rasterize( x0, y0, x1, y1, line );
rasterize( x1, y1, x2, y2, line );
rasterize( x2, y2, x3, y3, line );
rasterize( x3, y3, x0, y0, line );
for(y=0;y<NLINES;y++)
if(line[2*y+1]>line[2*y+0]) for(x=line[2*y+0]; x<=line[2*y+1]; x++) frBuf[SCR_WD*y+x]=c;
}
void cullQuads(int *v)
{
// backface culling
numVisible=0;
for(int i=0;i<numQuads;i++) {
if(bfCull) {
int x1 = v[3*quads[4*i+0]+0]-v[3*quads[4*i+1]+0];
int y1 = v[3*quads[4*i+0]+1]-v[3*quads[4*i+1]+1];
int x2 = v[3*quads[4*i+2]+0]-v[3*quads[4*i+1]+0];
int y2 = v[3*quads[4*i+2]+1]-v[3*quads[4*i+1]+1];
int z = x1*y2-y1*x2;
if(z<0) sortedQuads[numVisible++] = i;
} else sortedQuads[numVisible++] = i;
}
int i,j,zPoly[numVisible];
// average Z of the polygon
for(i=0;i<numVisible;++i) {
zPoly[i] = 0;
for(j=0;j<4;++j) zPoly[i] += v[3*quads[4*sortedQuads[i]+j]+2];
}
// sort by Z
for(i=0;i<numVisible-1;++i) {
for(j=i;j<numVisible;++j) {
if(zPoly[i]<zPoly[j]) {
SWAP_INT(zPoly[j],zPoly[i]);
SWAP_INT(sortedQuads[j],sortedQuads[i]);
}
}
}
}
void drawQuads(int *v2d)
{
for(int i=0;i<numVisible;i++) {
int q = sortedQuads[i];
int v0 = quads[4*q+0];
int v1 = quads[4*q+1];
int v2 = quads[4*q+2];
int v3 = quads[4*q+3];
drawQuad( v2d[2*v0+0],v2d[2*v0+1], v2d[2*v1+0],v2d[2*v1+1], v2d[2*v2+0],v2d[2*v2+1], v2d[2*v3+0],v2d[2*v3+1], quadColor[q]);
}
}
// animated checkerboard pattern
void backgroundChecker(int i)
{
int x,y,xx,yy, xo,yo;
xo = 25*fastSin(4*i)/256+50;
yo = 25*fastSin(5*i)/256+50+yFr;
for(y=0;y<NLINES;y++) {
yy = (50+y+yo+yFr)% 32;
for(x=0;x<WD_3D;x++) {
xx = (50+x+xo)% 32;
frBuf[SCR_WD*y+x] = ((xx<16 && yy<16) || (xx>16 && yy>16)) ? RGB(40,40,20) : RGB(80,80,40);
}
}
}
void backgroundPattern(int i, const unsigned short *pat)
{
int x,y,xp,yp;
xp = 25*fastSin(4*i)/256+50; // 256 not MAXSIN=255 to avoid jumping at max sin value
yp = 25*fastSin(5*i)/256+50+yFr;
for(y=0;y<NLINES;y++) for(x=0;x<WD_3D;x++) frBuf[SCR_WD*y+x] = pat[((y+yp)&0x1f)*32 + ((x+xp)&0x1f)];
}
// ------------------------------------------------
typedef struct{
int16_t x,y,z;
int16_t x2d,y2d, x2dOld,y2dOld;
}Star;
#define NUM_STARS 150
Star stars[NUM_STARS];
int starSpeed = 20;
int rand3d(int min, int max)
{
return min + rand() % (max+1 - min);
}
void initStar(int i)
{
stars[i].x = rand3d(-500, 500);
stars[i].y = rand3d(-500, 500);
stars[i].z = rand3d(100, 2000);
// remove stars from the center
if(stars[i].x<80 && stars[i].x>-80) stars[i].x=80;
if(stars[i].y<80 && stars[i].y>-80) stars[i].y=80;
}
int16_t rotZ = 1;
void updateStars()
{
int16_t i,x,y;
for(i=0; i<NUM_STARS; i++) {
if(rotZ) {
x = stars[i].x;
y = stars[i].y;
stars[i].x = (x*254 - y*2)/MAXSIN;
stars[i].y = (y*254 + x*2)/MAXSIN;
}
stars[i].z -= starSpeed;
stars[i].x2d = WD_3D/2 + 100 * stars[i].x / stars[i].z;
stars[i].y2d = HT_3D/2 + 100 * stars[i].y / stars[i].z;
if(stars[i].x2d>WD_3D || stars[i].x2d<0 || stars[i].y2d>HT_3D || stars[i].y2d<0) {
initStar(i);
stars[i].x2d = WD_3D/2 + 100 * stars[i].x / stars[i].z;
stars[i].y2d = HT_3D/2 + 100 * stars[i].y / stars[i].z;
stars[i].x2dOld = stars[i].x2d;
stars[i].y2dOld = stars[i].y2d;
}
}
}
void initStars()
{
for(int i=0; i<NUM_STARS; i++) initStar(i);
updateStars();
for(int i=0; i<NUM_STARS; i++) {
stars[i].x2dOld = stars[i].x2d;
stars[i].y2dOld = stars[i].y2d;
}
}
void backgroundStars(int f)
{
int i;
for(i=0; i<NLINES*WD_3D; i++) frBuf[i] = CL_BLACK;
for(i=0; i<NUM_STARS; i++) {
int r = 255-stars[i].z/5;
if(r>255) r=255;
if(r<40) r=40;
uint16_t col = RGB(r,r,r);
int x = stars[i].x2d;
int y = stars[i].y2d - yFr;
if(x>=0 && x<WD_3D && y>0 && y<NLINES) frBuf[SCR_WD*y+x] = col;
}
}
// ------------------------------------------------
int t=0;
void render3D(void)
{
int cos0,sin0,cos1,sin1;
int i,x0,y0,z0,fac,distToObj;
int camZ = 200;
int scaleFactor = HT_3D/4;
int near = 300;
if(t++>360) t-=360;
distToObj = 150 + 300*fastSin(3*t)/MAXSIN;
cos0 = fastCos(rot0);
sin0 = fastSin(rot0);
cos1 = fastCos(rot1);
sin1 = fastSin(rot1);
for(i=0;i<numVerts;i++) {
x0 = verts[3*i+0];
y0 = verts[3*i+1];
z0 = verts[3*i+2];
transVerts[3*i+0] = (cos0*x0 + sin0*z0)/MAXSIN;
transVerts[3*i+1] = (cos1*y0 + (cos0*sin1*z0-sin0*sin1*x0)/MAXSIN)/MAXSIN;
transVerts[3*i+2] = camZ + ((cos0*cos1*z0-sin0*cos1*x0)/MAXSIN - sin1*y0)/MAXSIN;
fac = scaleFactor * near / (transVerts[3*i+2]+near+distToObj);
projVerts[2*i+0] = (100*WD_3D/2 + fac*transVerts[3*i+0] + 100/2)/100;
projVerts[2*i+1] = (100*HT_3D/2 + fac*transVerts[3*i+1] + 100/2)/100;
}
if(bgMode==3) updateStars();
cullQuads(transVerts);
tft.startWrite();
for(i=0;i<HT_3D;i+=NLINES) {
yFr = i;
if(bgMode==0) backgroundPattern(t,pat2); else
if(bgMode==1) backgroundPattern(t,pat8); else
if(bgMode==2) backgroundPattern(t,pat7); else
if(bgMode==3) backgroundStars(t); else
if(bgMode==4) backgroundChecker(t);
drawQuads(projVerts);
// 原版STM32用DMA2D把frBuf搬到LCD的SDRAM显存,这里等价换成TFT_eSPI逐行推屏
tft.pushImage(0, yFr, SCR_WD, NLINES, frBuf);
}
tft.endWrite();
rot0 += 2;
rot1 += 4;
if(rot0>360) rot0-=360;
if(rot1>360) rot1-=360;
}
// --------------------------------------------------------------------------
unsigned long lastSwitchMs = 0;
const unsigned long SWITCH_INTERVAL_MS = 6000; // 每6秒切一次背景+物体,跟原版摇杆/定时器切换效果对应
// 原版armfly例程里这段是注释掉的(用的是LCD_DispStr+FONT_T),这里用TFT_eSPI小字体在
// 左下角实机复刻:当前帧耗时/fps(黄) + 本轮最小-最大耗时/fps(绿) + 面片总数/可见数(品红)
unsigned int msMin = 1000, msMax = 0;
// TJpg_Decoder解码一块(w x h)就回调一次,直接怼给TFT_eSPI画出来,
// 跟厂商demo(6_1_SD_Jpg/SD_Jpg.ino)里的tft_output写法一样
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
{
if (y >= tft.height()) return 0; // 超出屏幕就不用再画了,停止解码剩下的块
tft.pushImage(x, y, w, h, bitmap);
return 1;
}
// 视频帧是320x180,屏幕是320x240,上下各留30px黑边居中显示,不拉伸变形
void playVideoFrame()
{
unsigned long now = millis();
if (now - lastVideoFrameMs < (1000 / VIDEO_FPS)) return; // 按目标帧率节流,读卡+解码比这快就等着
lastVideoFrameMs = now;
char path[32];
snprintf(path, sizeof(path), "/bbb/frame%04d.jpg", videoFrameIdx);
int yOff = (SCR_HT - VIDEO_FRAME_H) / 2;
TJpgDec.drawSdJpg(0, yOff, path);
if (++videoFrameIdx > VIDEO_FRAME_COUNT) videoFrameIdx = 1;
}
// 只看IRQ脚有没有被拉低,不读坐标——点屏幕任意位置都能切换3D演示/SD视频这两个模式
void checkTouchToggle()
{
bool down = (digitalRead(TOUCH_IRQ) == LOW);
if (down && !touchWasDown && millis() - lastToggleMs > 400) {
mode = 1 - mode;
lastToggleMs = millis();
tft.fillScreen(TFT_BLACK);
if (mode == 1) videoFrameIdx = 1;
}
touchWasDown = down;
}
void run3DFrame()
{
if(millis() - lastSwitchMs >= SWITCH_INTERVAL_MS) {
lastSwitchMs = millis();
if(++bgMode>4) bgMode=0;
if(++object>3) object=0;
msMin = 1000;
msMax = 0;
}
switch(object) {
case 0:
numVerts = numVerts1;
verts = (int16_t*)verts1;
numQuads = numQuads1;
quads = (int16_t*)quads1;
quadColor = (uint16_t*)quadColor1;
bfCull = 1;
break;
case 1:
numVerts = numVerts2;
verts = (int16_t*)verts2;
numQuads = numQuads2;
quads = (int16_t*)quads2;
quadColor = (uint16_t*)quadColor2;
bfCull = 1;
break;
case 2:
default:
numVerts = numVerts3;
verts = (int16_t*)verts3;
numQuads = numQuads3;
quads = (int16_t*)quads3;
quadColor = (uint16_t*)quadColor3;
bfCull = 1;
break;
case 3:
numVerts = numVerts4;
verts = (int16_t*)verts4;
numQuads = numQuads4;
quads = (int16_t*)quads4;
quadColor = (uint16_t*)quadColor4;
bfCull = 0;
break;
}
unsigned long ms = millis();
render3D();
ms = millis() - ms;
if(ms<msMin) msMin=ms;
if(ms>msMax) msMax=ms;
char txt[40];
snprintf(txt, sizeof(txt), "%lu ms %lu fps ", ms, ms>0?1000/ms:0);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.drawString(txt, 0, SCR_HT-26);
snprintf(txt, sizeof(txt), "%u-%u ms %u-%u fps ", msMin, msMax, msMax>0?1000/msMax:0, msMin>0?1000/msMin:0);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.drawString(txt, 0, SCR_HT-18);
snprintf(txt, sizeof(txt), "total/vis %d / %d ", numQuads, numVisible);
tft.setTextColor(TFT_MAGENTA, TFT_BLACK);
tft.drawString(txt, 0, SCR_HT-10);
static unsigned long lastPrintMs = 0;
if(millis() - lastPrintMs >= 1000) {
lastPrintMs = millis();
Serial.printf("object=%d bgMode=%d frame=%lums ~%lufps quads %d/%d\n",
object, bgMode, ms, ms>0?1000/ms:0, numVisible, numQuads);
}
}
void setup()
{
Serial.begin(115200);
pinMode(TOUCH_IRQ, INPUT_PULLUP); // 空闲时IRQ被拉高,按下变低
// SD要在TFT前面初始化,跟厂商demo顺序一致;SD卡没插/坏了也不阻塞3D演示,只是切不过去视频模式
if (!SD.begin(SD_CS)) {
Serial.println("SD.begin failed! (视频模式会用不了,3D演示不受影响)");
} else {
Serial.println("SD OK");
}
tft.begin();
tft.setRotation(1); // 320x240 横屏,跟同目录其它工程一致
tft.setSwapBytes(true); // 配合上面RGB()宏生成的颜色顺序,TJpg_Decoder也吃这个设置
tft.fillScreen(TFT_BLACK);
tft.setTextFont(1);
tft.setTextSize(1);
TJpgDec.setJpgScale(1);
TJpgDec.setCallback(tft_output);
initStars();
lastSwitchMs = millis();
}
void loop()
{
checkTouchToggle();
if (mode == 0) run3DFrame();
else playVideoFrame();
}
见 下载完整烧录包 ZIP,提供四种方式,按自己趁手的选一种就行:
打开 01_Arduino_INO 里的 .ino,板子选普通ESP32 Dev Module,直接编译上传。
02_PlatformIO_Project 完整工程,pio run --target upload 一条命令搞定。
03_BIN_Firmware 下已编译好的 .bin,不用装Arduino IDE,直接用esptool烧。
打开网页烧录器(已实测),浏览器里点按钮就能烧,什么软件都不用装。
网页烧录器使用提醒:只支持电脑端Chrome/Edge浏览器(基于Web Serial API);客户直接从本站打开即可。本地离线测试时要起静态服务器打开(python -m http.server 8080),不能直接双击html文件。USB线必须是数据线,充电线没有数据针脚,电脑设备管理器里不会出现串口。
✓ 已实测通过:普通ESP32通过网页一键烧录merged_firmware_0x0.bin成功,写入437760字节,耗时约24.8秒。