基于ESP32的圆形显示屏圣诞主题互动式雪球

描述



 

用一款以圣诞为主题的互动式雪球让节日氛围鲜活起来!项目使用矽递科技(Seeed Studio)圆形显示屏和(XIAO)ESP32S3开发板打造出极具视觉吸引力的雪景动画,包含动态飘落的雪花、风效以及触摸交互功能。凭借双缓冲技术实现的流畅动画,该项目能提供专业且无闪烁的体验。

Seeed Studio XIAO系列是小型开发板,共享类似的硬件结构,尺寸实际上是拇指大小。这里的代号“小”代表它的一半特征“小”,另一半将是“羊角面包”。

开发板

Seeed Studio XIAO ESP32S3 Sense集成了摄像头传感器、数字麦克风和SD卡支持。结合嵌入式ML计算能力和摄影能力,这款开发板是使用智能语音和视觉AI的绝佳工具。

开发板

 

开发特点:

 

动态雪景动画:模拟飘落的雪花颗粒,其速度和风效均可调节。

触摸交互:只需轻触屏幕,即可循环切换三张漂亮的圣诞主题背景图。

流畅渲染:采用双缓冲技术实现无缝视觉效果,无闪烁现象。

可定制背景:轻松添加自己的 PNG 图像,对雪球进行个性化设置。

 

 

你将学到的内容:

 

如何将矽递科技圆形显示屏与xiao ESP32S3 开发板配合使用。

利用 TFT_eSPI 库实现双缓冲以呈现流畅动画。

使用 lv_xiao_round_screen 库处理触摸输入。

模拟粒子效果以实现逼真的雪景动画。

 

 

环境准备

 

硬件:对于该项目,我们需要这些设备:适用于晓开发板的矽递科技圆形显示屏、XIAO ESP32S3 开发板,我选用xiao ESP32S3 开发板是因为内存方面的考虑。PNGDEC(PNG 解码库)运行大约需要 40KB 的内存。

 

软件准备:要使用圆形显示屏,请前往 “晓开发板圆形显示屏入门” 页面安装必要的库。尝试运行一些示例,看看一切是否运行正常。

 

库:对于这个项目,我们将使用随适用于晓开发板的矽递科技圆形显示屏附带的库。按照 “晓开发板圆形显示屏入门” 教程中的规定安装所有库。之后,你还需要以下内容:

PNGdec 库。

更新 LVGL 库(或者不安装来自矽递科技 GitHub 的那个版本)

 

图像:我们的图像是存储在闪存数组中的 PNG 图像,使用 PNGdec 库进行显示。所有图像都必须是 PNG 格式。以下是我使用过的图像 —— 全部由人工智能生成。

 

 

我们需要准备好背景图像,以便 TFT_eSPI 库能够显示它们,并且这些图像能很好地适配晓开发板的圆形显示屏。

 

 

准备图像

 

调整图像大小,我们的XIAO开发板圆形显示屏分辨率为 240×240。我们需要对图像进行尺寸调整。下面我将展示如何使用 GIMP(一款图像处理软件)来操作。

 

1.打开图像

 

2.选择 “图像”>“缩放图像”

 

开发板

 

3.将宽度和高度都设置为 240。由于 “保持比例” 选项(链条图标所示)已被选中,一旦你更改了宽度,高度也会相应地改变。

 

开发板

 

4.点击 “缩放” 按钮。

 

 

 

5.保存图像(我打算覆盖原来的图像)。

 

开发板

 

现在图像已经准备好了,让我们来创建闪存数组吧。

 

 

创建闪存数组

 

注意:这些操作说明包含在 TFT_eSPI 库的 Flash_PNG 示例当中。要创建闪存数组,进入 “文件转 C 语言风格数组转换器”。

 

创建数组的步骤如下:

1、使用 “浏览” 功能上传图像。在上传图像之后……

 

 

 

2、我们需要设置一些选项

 

开发板

 

所有其他选项都会变灰(即不可用、无法进行设置操作)。

 

开发板

 

3、让我们将数据类型更改为字符型(char)。

 

开发板

 

4、点击 “转换” 按钮。这将会把图像转换为数组。

 

开发板

 

5、现在你可以按下 “另存为文件” 按钮来保存你的图像,并将其添加到你的 Arduino(开源电子原型平台)代码中,或者按下 “复制到剪贴板” 按钮。

 

如果你选择 “复制到剪贴板”,那么你需要点击 Arduino 编辑器右侧的三个点(省略号图标),然后选择 “新建标签页”。

 

开发板

 

给它取个名字(一般来说是你的图像名加上.h 扩展名)。

 

开发板

 

最终你所有的图像都会以.h 文件的形式存在。

 

 

代码

 

以下是对代码主要功能的一些解释,代码中也包含了一些注释。

 

头文件与库

我们首先引入一些库:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

#include #include #include 
#include "background1.h"#include "background2.h"#include "background3.h"
#define USE_TFT_ESPI_LIBRARY#include "lv_xiao_round_screen.h"

(左右移动查看全部内容)

 

请记住,你需要安装矽递科技(Seeed Studio)相关的库。

 

背景图像以下是管理背景图像的函数:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

struct Background {  const uint8_t *data;  size_t size;};
const Background backgrounds[] = {    {(const uint8_t *)background1, sizeof(background1)},    {(const uint8_t *)background2, sizeof(background2)},    {(const uint8_t *)background3, sizeof(background3)},};

(左右移动查看全部内容)

 

结构体:每个背景图像都作为一个 Background 结构体进行存储,该结构体包含:

data:指向 PNG 数据的指针。

size:PNG 文件的大小。

 

数组:backgrounds 数组存储了所有的背景图像。currentBackground 变量用于追踪当前显示的背景图像。

 

雪花粒子模拟

1. 粒子初始化

  •  
  •  
  •  
  •  
  •  
  •  
  •  

void initParticles() {  for (int i = 0; i < numParticles; i++) {    particles[i].x = random(0, sprite.width());    particles[i].y = random(0, sprite.height());    particles[i].speed = random(3, 8);  }}

(左右移动查看全部内容)

 

它使用随机位置和速度来初始化 numParticles 个粒子。

 

2. 粒子更新

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

void updateParticles() {  for (int i = 0; i < numParticles; i++) {    particles[i].speed += random(-1, 2); // 速度变化    particles[i].speed = constrain(particles[i].speed, 3, 8);    particles[i].y += particles[i].speed; // 向下移动    particles[i].x += random(-1, 2);      // 风效影响    // 循环逻辑    if (particles[i].y > sprite.height()) {      particles[i].y = 0;      particles[i].x = random(0, sprite.width());      particles[i].speed = random(3, 8);    }    if (particles[i].x < 0) particles[i].x = sprite.width();    if (particles[i].x > sprite.width()) particles[i].x = 0;  }}

(左右移动查看全部内容)

 

通过以下方式更新粒子位置:

下落效果:每个粒子向下移动。

风效影响:添加轻微的水平偏移。

循环机制:当粒子从底部离开时,重置到顶部。

 

3. 粒子渲染

  •  
  •  
  •  
  •  
  •  

void renderParticlesToSprite() {  for (int i = 0; i < numParticles; i++) {    sprite.fillCircle(particles[i].x, particles[i].y, 2, TFT_WHITE);  }}

(左右移动查看全部内容)

 

它将每个粒子渲染为一个小的白色圆圈。

 

PNG 解码

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

int16_t rc = png.openFLASH((uint8_t *)backgrounds[currentBackground].data,                           backgrounds[currentBackground].size,                           pngDrawToSprite);if (rc!= PNG_SUCCESS) {  Serial.println("Failed to open PNG file!");  return;}png.decode(NULL, 0);

(左右移动查看全部内容)

 

使用 png.openFLASH() 函数加载并解码当前的背景 PNG 图像。

 

触摸交互

  •  
  •  
  •  
  •  

if (chsc6x_is_pressed()) {  currentBackground = (currentBackground + 1) % numBackgrounds; // 循环切换背景  delay(300); // 去抖动}

(左右移动查看全部内容)

 

使用 chsc6x_is_pressed() 检测触摸事件,并通过递增 currentBackground 变量来切换背景图像。

 

设置与循环

设置部分:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

void setup() {  Serial.begin(115200);  tft.begin();  tft.fillScreen(TFT_BLACK);  sprite.createSprite(240, 240); // 匹配显示屏尺寸  pinMode(TOUCH_INT, INPUT_PULLUP);  Wire.begin();  initParticles();}

(左右移动查看全部内容)

 

初始化显示屏、触摸输入以及雪花粒子。

 

主循环:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

void loop() {  sprite.fillScreen(TFT_BLACK);  // 渲染背景和雪花  int16_t rc = png.openFLASH((uint8_t *)backgrounds[currentBackground].data,                             backgrounds[currentBackground].size,                             pngDrawToSprite);  if (rc == PNG_SUCCESS) {    png.decode(NULL, 0);    updateParticles();    renderParticlesToSprite();    sprite.pushSprite(0, 0);  }  // 处理触摸输入  if (chsc6x_is_pressed()) {    currentBackground = (currentBackground + 1) % numBackgrounds;    delay(300);  }  delay(10); // 约100帧每秒}

(左右移动查看全部内容)

 

清除图像缓存(sprite),渲染当前帧(背景 + 粒子),并检查用户输入。

 

双缓冲

为了减少雪花闪烁并提高动画的流畅度,我们使用双缓冲技术。

 

这使得我们能够在屏幕外的缓冲区进行绘制,然后再将其显示在屏幕上。

 

本项目中的双缓冲

在这个项目中,TFT_eSPI 库的 TFT_eSprite 类实现了双缓冲。

 

1. 图像缓存(sprite)创建

在 setup() 函数中创建图像缓存(屏幕外缓冲区):

  •  

sprite.createSprite(240, 240); // 匹配显示屏尺寸

(左右移动查看全部内容)

 

2. 绘制缓冲区

所有绘制操作(背景渲染和雪花粒子动画)都在图像缓存(sprite)上进行:

  •  
  •  

sprite.fillScreen(TFT_BLACK); // 清除图像缓存renderParticlesToSprite();   // 绘制雪花粒子

(左右移动查看全部内容)

 

3. 更新显示

在图像缓存中完整绘制完一帧后,通过一次操作将其推送到显示屏上:

  •  

sprite.pushSprite(0, 0);

(左右移动查看全部内容)

 

这会立即将缓冲区的内容传输到屏幕上。

 

4. 复用

在循环开始时清除图像缓存,以便每一帧都能复用它:

  •  

sprite.fillScreen(TFT_BLACK);

(左右移动查看全部内容)

 

使用双缓冲的优势

流畅的雪花动画:下落的雪花粒子能够无缝更新,不会出现闪烁现象。

动态背景切换:触摸触发的背景切换能够在无可见延迟或瑕疵的情况下完成。

高效渲染:在内存(RAM)中进行绘制比逐行直接更新显示屏要快。

 

 

总结

 

我希望有人能制作一个 3D 球体,把适用于晓开发板的矽递科技圆形显示屏放在里面,然后将其挂在圣诞树上。

 

我也希望修改代码,使其能从 SD 卡加载图像,而不是使用闪存数组来存储图像。

 

希望你们喜欢这个项目,为你们的圣诞节增添一点奇妙氛围。

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分