news 2026/7/29 15:41:18

ESP32-S3驱动VGA显示器:I2S+DMA方案实现与优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ESP32-S3驱动VGA显示器:I2S+DMA方案实现与优化

1. 项目概述:当ESP32-S3遇上VGA

最近在捣鼓FireBeetle 2这块板子,核心是ESP32-S3,性能比之前的ESP32强不少,双核240MHz,还带PSRAM。我琢磨着,能不能用它来点“复古”的玩法,比如驱动一个VGA显示器。现在满世界都是HDMI,找个VGA接口的显示器反而有点怀旧的味道了。这个项目的核心,就是让这块主打物联网的MCU,去干一件它原本不太擅长的事:生成精确的模拟视频时序信号,并输出到那个熟悉的15针D-Sub接口上。

这听起来有点跨界,但实现起来特别有意思。它不依赖于任何额外的专用视频芯片,纯粹靠ESP32-S3的GPIO、I2S和DMA这些外设,通过软件“bit-banging”的方式,实时合成VGA所需的行同步、场同步和RGB模拟信号。最终,你会在一个640x480@60Hz(或者更低分辨率)的VGA显示器上,看到由这块物联网芯片绘制出的图形、文字甚至是简单的动画。这不仅仅是让一个旧显示器焕发新生,更是对微控制器实时能力、时序精度和资源管理的一次深度探索。如果你对底层硬件驱动、数字信号合成或者只是想给手头的FireBeetle 2找个好玩的“输出窗口”,那这个项目会给你带来不少动手的乐趣和成就感。

2. 核心原理与方案选型

2.1 VGA信号时序深度解析

要让VGA显示器正常显示,我们必须扮演好“信号源”的角色,严格遵守它规定的“语言”(时序)。VGA信号本质上是模拟的,但对于我们数字芯片来说,需要生成精确的数字时序来驱动数模转换(DAC)或电阻网络,以产生对应的模拟电压。

一个完整的VGA帧(Frame)由逐行扫描(Scanline)构成。以最经典的640x480@60Hz模式为例,它的时序参数是公开标准。我们需要关注几个关键区间:

  • 有效显示区域(Active Video):640个像素宽,480行高。这是我们实际可以绘制内容的区域。
  • 消隐区间(Blanking Interval):这包括了行消隐(Horizontal Blanking)和场消隐(Vertical Blanking)。在CRT时代,这是让电子束从屏幕右侧回到左侧(行回扫),或从底部回到顶部(场回扫)所需的时间。对于数字系统,这段时间我们不发送像素数据,但必须维持同步信号。
  • 同步脉冲(Sync Pulse):行同步(HSYNC)和场同步(VSYNC)是负极性脉冲(通常低电平有效)。它们告诉显示器何时开始新的一行和新的一个帧。

具体到640x480@60Hz,一个典型的时序参数如下(单位:像素时钟周期,对于25.175MHz的像素时钟,每个周期约39.7纳秒):

  • 行时序(Horizontal Timing)
    • 有效像素(Visible Area): 640
    • 前沿(Front Porch): 16
    • 同步脉冲(Sync Pulse): 96
    • 后沿(Back Porch): 48
    • 整行总计(Total Pixels per Line): 640 + 16 + 96 + 48 =800
  • 场时序(Vertical Timing)
    • 有效行数(Visible Lines): 480
    • 前沿(Front Porch): 10
    • 同步脉冲(Sync Pulse): 2
    • 后沿(Back Porch): 33
    • 整场总计(Total Lines per Frame): 480 + 10 + 2 + 33 =525

我们的任务就是以25.175MHz的速率(或一个近似的、稳定的时钟),在每个像素周期内,通过GPIO输出代表RGB颜色的数字值,并在特定的800像素周期中的第656-752周期拉低HSYNC线,在525行中的第490-492行拉低VSYNC线。

注意:实际上,由于ESP32-S3很难生成精确的25.175MHz时钟,我们通常会选择一个它能够稳定输出的、接近的时钟频率(例如通过I2S模块生成20MHz或40MHz),然后调整时序参数中的计数值来适配,只要最终的行频和场频在显示器的容差范围内即可。这是一个关键的妥协点。

2.2 为什么选择I2S+DMA方案?

在ESP32-S3上实现VGA输出,主要有两种思路:一是纯GPIO位操作(Bit Banging),二是利用外设。纯GPIO操作对CPU占用率极高,几乎需要全力运行才能维持时序,无法做其他事情。因此,更实用的方案是利用ESP32-S3的I2S(Inter-IC Sound)外设的LCD模式,配合DMA(直接内存访问)。

I2S LCD模式原本设计用于驱动LCD屏的并行接口(如8080或RGB接口)。在这个模式下,I2S模块可以配置为一个并行的、带时钟的数据发送器。我们可以将像素数据(例如RGB332格式:3位红、3位绿、2位蓝,共8位)放入一个缓冲区,I2S模块会在其自身时钟(BCK)驱动下,自动地、持续地将缓冲区中的数据通过一组GPIO(数据线)发送出去,同时还可以生成行同步(HSYNC)和场同步(VSYNC)信号。整个过程由DMA自动从内存搬运数据到I2S FIFO,无需CPU干预。

选择这个方案的核心理由

  1. 解放CPU:一旦DMA和I2S配置好并启动,CPU只需要在每帧或每行结束时(通过中断)更新帧缓冲区的内容即可,其余时间可以处理应用逻辑、网络连接等,实现了“并行”处理。
  2. 时序精准:I2S的时钟由APLL或外部晶振分频而来,非常稳定,能保证像素时钟频率的稳定性,这是生成稳定画面的基础。
  3. 资源匹配:VGA的像素输出是持续的、流式的,这与I2S发送音频或LCD数据的流式特性完美契合。DMA的自动搬运机制也为此类数据流量身定做。

因此,我们的方案架构就明确了:使用ESP32-S3的I2S1(因为I2S0通常用于音频)的LCD模式,配置DMA双缓冲区,将帧缓冲区中的RGB像素数据,以稳定的像素时钟频率,通过8-16根GPIO数据线输出,同时用额外的GPIO生成HSYNC和VSYNC信号。

3. 硬件连接与电路设计

3.1 FireBeetle 2引脚分配与功能定义

FireBeetle 2基于ESP32-S3,拥有丰富的GPIO。我们需要分配三组引脚:

  1. RGB颜色数据线(Data Pins):这是输出像素颜色的通道。颜色深度取决于你使用的引脚数量。最常见的是RGB332(8位色),需要8根数据线。也可以使用RGB565(16位色),需要16根数据线。RGB332颜色数较少(256色)但节省引脚和带宽,RGB565颜色更丰富(65536色)但要求更高。对于初试和640x480分辨率,RGB332是更稳妥的选择。
  2. 同步信号线(Sync Pins):两根,分别用于HSYNC(行同步)和VSYNC(场同步)。
  3. 可选时钟线(Clock Pin):有时会专门用一根GPIO输出像素时钟(PCLK),但我们的I2S LCD模式通常使用I2S的位时钟(BCK)作为像素时钟,所以这根线可能不是必须的,具体看驱动库的实现。

一个基于RGB332(8位)的引脚分配示例如下(引脚编号为ESP32-S3的GPIO号,你需要对照FireBeetle 2的板载丝印找到对应物理引脚):

  • 数据线 D0-D7:例如使用 GPIO 33, 34, 35, 36, 37, 38, 39, 40。这8个引脚最好选择同一组连续的GPIO,有利于I2S配置。
  • HSYNC:例如 GPIO 41。
  • VSYNC:例如 GPIO 42。
  • PCLK (可选):如果库要求,可以使用 GPIO 21(I2S的BCK输出)。

实操心得:尽量选择ESP32-S3的“Strapping GPIO”之外的引脚,并避免使用那些在启动时有特殊功能的引脚(如GPIO0, GPIO45等)。FireBeetle 2的引脚布局可能与其他ESP32-S3开发板不同,务必查阅其原理图,确认你选择的引脚没有被板载LED、按钮或其他外设占用。

3.2 VGA接口与电阻网络DAC

VGA接口的15个针脚中,我们只需要用到其中几个:

  • Pin 1 (Red): 红色模拟信号
  • Pin 2 (Green): 绿色模拟信号
  • Pin 3 (Blue): 蓝色模拟信号
  • Pin 13 (HSYNC): 行同步(数字信号)
  • Pin 14 (VSYNC): 场同步(数字信号)
  • Pin 5, 6, 7, 8, 10 (GND): 接地

ESP32-S3的GPIO输出是数字信号(0V或3.3V),而VGA的RGB输入要求是0-0.7V范围内的模拟电压。因此,我们需要一个简单的电阻分压网络(Resistor DAC)来将数字电平转换为模拟电压。

对于RGB332的8位数据,我们可以为每个颜色通道(R, G, B)使用不同阻值的电阻来构建一个3位的DAC。以红色通道为例(使用数据线的高3位,例如D7, D6, D5):

  • 假设D7是最高位(MSB),D5是最低位(LSB)。
  • 为D7连接一个阻值最小的电阻(例如100Ω)到VGA的Red引脚。
  • 为D6连接一个两倍阻值的电阻(200Ω)。
  • 为D5连接一个四倍阻值的电阻(400Ω)。
  • 所有电阻的另一端连接到对应的GPIO。
  • VGA的Red引脚再通过一个75Ω的电阻连接到地(GND)。这个75Ω是VGA显示器输入端的特征阻抗,用于阻抗匹配,防止信号反射,非常重要

这样,当GPIO输出高电平(3.3V)时,电流会流过电阻网络,在75Ω电阻上产生分压。不同的位组合会产生8种不同的电压水平(从0V到约0.7V),代表8种红色强度。绿色和蓝色通道同理。

一个简化的、更常用的方案是使用“R-2R”电阻梯形网络,它只需要两种阻值的电阻(R和2R),理论精度更好。但对于VGA这种对精度要求不是极端高的场景,前述的加权电阻网络更简单直观。你也可以为每个通道只使用1根GPIO(即仅输出2阶亮度),但这会严重损失颜色。

注意事项:电阻的精度(通常选用1%精度的金属膜电阻)和75Ω的终端电阻对画面质量(尤其是颜色准确性和灰阶)有直接影响。如果画面出现重影、颜色偏色或亮度不对,首先检查电阻网络和连接。另外,所有地线(GND)必须可靠连接,形成共同的参考地平面,这是模拟信号稳定的基础。

4. 软件驱动与库的配置

4.1 ESP32-S3显示驱动框架选择

在Arduino IDE或PlatformIO环境下,有几个优秀的库可以简化我们的工作。最主流的是ESP32_8BIT_CVBS库的VGA分支,或者专门为VGA优化的ESP32-VGA-GFX库。这些库已经封装好了I2S LCD模式、DMA缓冲区和基本时序,我们只需要关注引脚配置和绘图操作。

这里以ESP32_8BIT_CVBS库为例(它同时支持复合视频CVBS和VGA)。你需要从GitHub上手动安装这个库到你的Arduino库目录。

核心配置步骤:

  1. 引入库和定义:在代码开头包含必要的头文件,并定义你的引脚映射。

    #include <ESP32_8BIT_CVBS.h> // 创建一个显示对象,指定为VGA模式 ESP32_8BIT_CVBS display; // 定义你的引脚(根据之前的硬件连接修改) // 数据引脚:通常库要求连续的8个GPIO #define PIN_RED_BIT_0 33 // 红色最低位(根据你的电阻网络连接调整) #define PIN_GREEN_BIT_0 34 #define PIN_BLUE_BIT_0 35 // ... 假设你使用RGB111,实际库可能要求8位数据线连续排列 // 更常见的做法是库内部有默认引脚映射,你需要查看库的示例代码或头文件 #define PIN_HSYNC 41 #define PIN_VSYNC 42 #define PIN_PCLK 21 // 像素时钟,可能不需要单独连接

    实际上,许多库在begin()函数里通过一个配置结构体来设置引脚,而不是用#define

  2. 初始化显示:在setup()函数中,初始化显示对象,设置分辨率和颜色模式。

    void setup() { // 初始化串口用于调试 Serial.begin(115200); // 初始化VGA显示 // 使用640x480分辨率,RGB332颜色模式(如果库支持) // 库可能会自动选择最接近的可用分辨率 display.begin(); // 或者使用更详细的配置 // display.begin(RESOLUTION_640x480, COLOR_RGB332); // 设置背景色和文本颜色 display.fillScreen(0x00); // 黑色 display.setTextColor(0xFF, 0x00); // 白色文字,黑色背景 display.setTextSize(2); display.setCursor(50, 200); display.println("Hello, VGA!"); }

    库的begin()函数内部会完成所有繁重的工作:配置I2S时钟、设置DMA描述符、分配帧缓冲区、启动I2S传输任务。

4.2 帧缓冲区管理与绘图API

库通常会提供一个或多个帧缓冲区(Framebuffer)。绘图操作(如画点、画线、填充、显示文字)实际上是在修改这个位于内存中的缓冲区。然后,I2S的DMA会自动将这个缓冲区的内容发送出去。

双缓冲机制:为了避免屏幕撕裂(Tearing),高级的驱动会使用双缓冲区。一个缓冲区(前缓冲区)正在被DMA读取发送到屏幕,另一个缓冲区(后缓冲区)供CPU绘制下一帧。当后缓冲区绘制完成后,通过一个交换操作,让DMA开始读取新的缓冲区。ESP32_8BIT_CVBS库可能简化了这个过程,但对于快速动画,理解这个概念很重要。

基本绘图操作

void loop() { // 1. 清屏或绘制背景 display.fillScreen(0x00); // 黑色 // 2. 画图形 display.drawRect(10, 10, 100, 50, 0xF0); // 画一个青色矩形框 display.fillCircle(200, 150, 30, 0xE0); // 画一个黄色实心圆 // 3. 画线 display.drawLine(0, 0, 639, 479, 0x1C); // 画一条绿色对角线 // 4. 显示文本 display.setTextColor(0xFF); display.setCursor(250, 400); display.printf("Frame: %lu", millis() / 1000); // 5. 交换缓冲区(如果库支持且启用双缓冲) // display.swapBuffers(); delay(16); // 约60FPS }

这些drawXXXfillXXX函数,以及setCursorprintln等文本函数,其底层都是在计算像素位置,并将对应的颜色值写入帧缓冲区的特定位置。颜色值0xF00xE0等是RGB332格式的十六进制表示,你需要一个颜色拾取工具来帮助你转换。

实操心得:频繁调用fillScreen()或绘制大面积的图形会消耗大量CPU时间,可能影响帧率。对于动态内容,尽量只更新屏幕上发生变化的部分(脏矩形更新)。另外,ESP32-S3的PSRAM(如果板载有)可以用来存储更大的帧缓冲区或图形资源(如字库、图片),从而释放宝贵的内部SRAM。

5. 时序调试与常见问题排查

5.1 上电无显示与信号诊断

这是最令人头疼的第一步。连接好硬件,上电后屏幕一片漆黑或者显示“无信号”。

诊断流程:

  1. 检查基础连接:确保VGA线的15针插头与你的转接板或焊接点连接牢固,没有虚焊或短路。特别是5根地线(Pin 5,6,7,8,10)必须全部可靠接地。
  2. 验证同步信号:这是第一步。你需要一个逻辑分析仪或者一个简单的示波器(甚至可以用Arduino配合库模拟一个简易逻辑分析仪)。测量HSYNC和VSYNC引脚。
    • HSYNC:你应该能看到一个频率约为31.5kHz(640x480@60Hz的行频)的方波脉冲。脉冲低电平宽度应约为3.8微秒(96个像素时钟周期)。
    • VSYNC:频率应为60Hz,低电平脉冲宽度约为两行的时间(约64微秒)。 如果测不到同步信号,说明软件配置或引脚分配有误。检查代码中I2S和GPIO的初始化部分,确认HSYNC和VSYNC引脚模式已正确设置为输出,并且库的时序配置是正确的。
  3. 检查像素时钟和数据线:如果同步信号正常,但屏幕仍是黑屏或显示“超出频率范围”,则问题可能出在像素时钟或数据线上。
    • PCLK:测量像素时钟引脚(如果有),频率应接近25.175MHz(对于640x480)。ESP32-S3生成的时钟可能略有偏差(如20MHz或40MHz),但只要同步信号频率正确,许多显示器有一定的容忍度。
    • 数据线:用示波器观察RGB数据线。在行有效期间(HSYNC脉冲之后的后沿结束到下一个HSYNC脉冲的前沿开始),数据线上应该有不断变化的数字电平。如果数据线始终为低或高,说明帧缓冲区数据没有成功送到GPIO,或者DMA/I2S传输未启动。

一个关键技巧:编写一个简单的测试图案。在初始化后,不要绘制复杂图形,而是用纯色填充屏幕,比如display.fillScreen(0xE0);(黄色)。然后用万用表测量VGA接口的Red、Green、Blue引脚对地的直流电压。如果显示黄色,Red和Green引脚电压应该较高(约0.6V),Blue引脚电压应接近0V。这可以快速验证你的电阻网络DAC是否工作,以及颜色通道的映射是否正确。

5.2 画面抖动、撕裂与颜色异常解决

当屏幕有显示但质量不佳时,问题通常更微妙。

问题现象可能原因排查与解决思路
画面上下滚动或抖动VSYNC时序不准确,场频不稳定或超出显示器范围。检查代码中的场时序参数(总行数、同步脉冲、前后沿)。使用逻辑分析仪精确测量VSYNC频率,调整参数使其稳定在59.94Hz或60Hz。确保I2S的时钟源(如APLL)配置稳定。
画面水平方向有重影或拖尾阻抗不匹配或信号质量差。行频可能轻微偏移。1.确保75Ω终端电阻已正确焊接在VGA接口的RGB引脚与地之间,这是消除重影最关键的一步。
2. 检查RGB数据线到电阻网络的导线是否过长,尽量缩短。
3. 测量HSYNC频率,微调行时序参数。
颜色错误(如红色显示为蓝色)RGB数据线引脚映射错误。检查代码中颜色格式(RGB332)的位定义与硬件电阻网络的连接是否一致。例如,软件里红色最高位对应GPIO33,但硬件上这根线接到了Blue通道的最高位电阻上。重新核对并修正引脚分配。
颜色暗淡或发白电阻网络分压比计算错误,导致模拟电压峰值过低或过高。计算你的电阻网络在全部位为高电平(3.3V)时,在75Ω终端电阻上产生的压降。理论最大值应在0.7V左右。如果偏差太大,调整电阻值。例如,如果电压太低(颜色暗),减小串联电阻(如100Ω, 200Ω, 400Ω)的阻值。
屏幕撕裂(部分显示上一帧,部分显示下一帧)绘图速度慢于刷新速度,且未使用双缓冲。在DMA读取帧缓冲区的过程中修改了缓冲区数据。1. 优化绘图代码,减少单帧绘制时间。
2. 如果库支持,启用双缓冲区(display.setDoubleBuffer(true)或类似函数),并确保在完整绘制完一帧后调用缓冲区交换函数(display.swapBuffers())。
3. 将帧缓冲区分配到PSRAM(如果可用),因为PSRAM的带宽可能不同于内部SRAM,需参考库的说明。
出现“A fatal error occurred: Failed to connect to ESP32-S3”这是上传代码时的错误,与VGA输出无关。通常是因为开发板未进入下载模式,或串口被占用。1. 确保在上传时,按住FireBeetle 2的“BOOT”按钮,再按一下“RST”按钮,然后释放“RST”,再释放“BOOT”,使其进入下载模式。
2. 检查Arduino IDE或PlatformIO中选择的正确端口和板卡类型(FireBeetle 2 ESP32-S3)。
3. 关闭可能占用串口的其他软件(如串口监视器)。

5.3 性能优化与高级技巧

当基本显示稳定后,你可以追求更好的效果和更高的效率。

  1. 提升刷新率与分辨率:640x480@60Hz是ESP32-S3在RGB332下的一个舒适区。如果你想尝试更高分辨率(如800x600)或更高刷新率,会立刻面临带宽压力。像素时钟频率 = 水平总数 × 垂直总数 × 刷新率。800x600@60Hz需要大约40MHz的像素时钟,而RGB565(16位)模式则需要双倍的数据带宽。这可能会接近ESP32-S3 GPIO和I2S的极限,导致画面不稳定。优化方法是降低颜色深度(如用RGB332代替RGB565)或降低刷新率(如50Hz)

  2. 使用PSRAM扩展资源:FireBeetle 2如果搭载了PSRAM,那是巨大的优势。你可以将帧缓冲区、大型位图字体、图像资源全部放到PSRAM中。在Arduino中,使用ps_malloc()来分配内存。但要注意,访问PSRAM比内部SRAM慢,对于需要逐像素快速计算的图形操作,可能还是需要将活动数据放在内部SRAM。

  3. 实现硬件加速图形:ESP32-S3没有专用的2D图形加速器,但我们可以利用其强大的CPU和DSP指令进行优化。例如:

    • 矩形填充优化:使用memset()memcpy()函数(它们通常经过高度优化)来快速填充大块颜色,而不是用循环逐像素设置。
    • 阿尔法混合与图像合成:如果需要透明效果,可以预先计算好混合表,或者使用定点数运算来加速。
    • 使用LVGL等图形库:LVGL是一个开源的高效嵌入式图形库,已对ESP32平台有很好的支持。它提供了丰富的控件、动画和抗锯齿字体,并且其底层驱动框架可以适配我们的VGA显示驱动。将LVGL移植到你的VGA项目上,可以立刻获得一个现代、美观的GUI界面,远比从零绘制线条和文字强大。
  4. 降低CPU占用:核心原则是减少在loop()中每帧都必须执行的绘图量。使用“脏矩形”算法,只更新屏幕上发生变化的小区域。将静态的背景元素绘制到一个离屏缓冲区(或直接作为背景层),每帧只复制这个背景,再在上面绘制动态内容。

6. 项目扩展与创意应用

让VGA显示“Hello World”只是开始。FireBeetle 2 ESP32-S3具备Wi-Fi和蓝牙功能,这为VGA显示打开了无限的创意之门。

应用一:网络信息仪表盘利用ESP32-S3的Wi-Fi连接,你可以从互联网API(如天气、股票、新闻摘要)获取数据,然后在VGA屏幕上以自定义的仪表盘形式实时显示。这就像一个自制的“魔法镜子”精简版,但更具极客风格。你需要处理HTTP请求、JSON解析,并设计相应的文本和图形显示逻辑。

应用二:复古游戏机ESP32-S3的性能足以模拟一些经典的8位游戏机,或者运行专门为嵌入式系统编写的复古游戏(如DOOM的简化版)。VGA输出正好匹配这些游戏的原始显示风格。你可以连接USB游戏手柄或自制摇杆,将FireBeetle 2变成一个怀旧游戏盒子。这需要深入的游戏循环编程、精灵图渲染和用户输入处理。

应用三:设备调试与状态监视器为你其他复杂的电子项目(比如机器人、3D打印机)添加一个本地显示界面。通过串口、I2C或Wi-Fi,FireBeetle 2接收来自主设备的数据(如传感器读数、系统状态),并以图表、仪表或日志列表的形式在VGA屏幕上清晰展示。这比只看串口监视器的文本输出直观得多。

应用四:低分辨率数字相框虽然ESP32-S3处理高分辨率JPEG很吃力,但显示低分辨率(如320x240)的RAW位图或简单编码的图片是可行的。你可以从SD卡读取图片文件,解码后显示在屏幕上。甚至可以做一个简单的幻灯片播放应用。

在实现这些扩展应用时,你会遇到多任务处理的问题:Wi-Fi通信、数据解析、图形渲染都需要时间。务必合理使用FreeRTOS任务(xTaskCreate)将不同功能模块化,并注意任务间的通信(队列、信号量)和共享资源(如帧缓冲区)的互斥访问,避免出现竞态条件导致画面撕裂或系统崩溃。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/29 15:40:44

网盘下载速度慢?9大平台直链解析工具终极解决方案

网盘下载速度慢&#xff1f;9大平台直链解析工具终极解决方案 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 &#xff0c;支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘…

作者头像 李华
网站建设 2026/7/29 15:39:21

Arduino驱动8x8 RGB LED点阵:SPI通信与rgb_matrix库实战指南

1. 项目概述&#xff1a;点亮一块会变色的“像素画板”最近在整理工作室的元件盒&#xff0c;翻出来几块之前买的8*8三色全彩LED点阵模块。这玩意儿买的时候觉得挺酷&#xff0c;能显示各种颜色&#xff0c;但吃灰久了都快忘了怎么用了。正好手边有Arduino Uno&#xff0c;就想…

作者头像 李华
网站建设 2026/7/29 15:38:16

如何用Loop这款终极免费开源Mac窗口管理工具提升3倍工作效率

如何用Loop这款终极免费开源Mac窗口管理工具提升3倍工作效率 【免费下载链接】Loop Window management made elegant. 项目地址: https://gitcode.com/GitHub_Trending/lo/Loop 你是否厌倦了在Mac上不断拖拽窗口、调整大小、寻找丢失的应用程序&#xff1f;当浏览器、代…

作者头像 李华
网站建设 2026/7/29 15:37:48

看牙前最担心的事

在看牙前&#xff0c;最担心的其实是过程会不会很复杂&#xff0c;毕竟这是我第一次接触牙齿这些事。心里头总是有点紧张&#xff0c;怕自己应付不来。提前在网上查了不少资料&#xff0c;也看了不少人的分享&#xff0c;但还是觉得不够踏实。怕麻烦&#xff0c;也担心听不懂医…

作者头像 李华
网站建设 2026/7/29 15:37:06

如何快速解密加密音乐文件:Unlock Music 开源工具使用指南

如何快速解密加密音乐文件&#xff1a;Unlock Music 开源工具使用指南 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库&#xff1a; 1. https://github.com/unlock-music/unlock-music &#xff1b;2. https://git.unlock-music.dev/um/web 项目地址: h…

作者头像 李华