Arduino 开发
本章节包含以下部分,请按需阅读:
Arduino 入门教程
初次接触 Arduino ESP32 开发,想要快速上手?我们为您准备了一套通用的 入门教程。
- 第0节 认识 ESP32
- 第1节 安装和配置 Arduino IDE
- 第2节 Arduino 基础知识
- 第3节 数字输出/输入
- 第4节 模拟输入
- 第5节 脉冲宽度调制 (PWM)
- 第6节 串行通信 (UART)
- 第7节 I2C 通信
- 第8节 SPI 通信
- 第9节 Wi-Fi 基础用法
- 第10节 网页服务器
- 第11节 蓝牙 (Bluetooth)
- 第12节 LVGL 图形界面开发
- 第13节 综合项目
请注意:该教程使用 ESP32-S3-Zero 作为教学示例,所有硬件代码均基于其引脚布局。在动手实践前,建议您对照手中的开发板引脚图,确认引脚配置无误。
配置开发环境
1. 安装和配置 Arduino IDE
请参考 安装和配置 Arduino IDE 教程 下载安装 Arduino IDE 并添加 ESP32 支持。
| 板名称 | 板安装要求 | 版本号要求 |
|---|---|---|
| esp32 by Espressif Systems | “离线”安装/“在线”安装 | 3.3.7 |
Arduino 设置:

2. 运行示例
Arduino 示例程序:ESP32-S3-RGB-Matrix 示例程序-GitHub Arduino 示例程序:ESP32-S3-RGB-Matrix 示例程序-Gitee
下面给出每个示例的目的、要点说明与运行效果(以便快速上手)。
| 示例程序 | 基础例程说明 |
|---|---|
| 01_SimpleTestShapes | 简单形状绘画 |
| 02_PatternPlasma | 等离子特效 |
| 03_DoubleBuffer | 双缓冲测试,绘制动态图形 |
| 04_OtherShiftDriverPanel | 使用驱动芯片驱动屏幕 |
| 05_AnimatedGIFPanel_SD | 读取 SD 卡中的 GIF 图片并显示 |
| 06_BitmapIcons | 显示bmp 图片 |
| 07_Pixel_Mapping_Test | 展示HUB75基本控制逻辑 |
提示
注意:
- 若驱动P4系列的屏幕,注意在代码中添加mxconfig.driver = HUB75_I2S_CFG::SHIFTREG;,否则会导致显示异常。
01_SimpleTestShapes
【代码分析】
loop():按顺序执行文字绘制、纯色填充与清屏操作,用于快速验证面板的基础显示功能。drawText(wheelval):根据wheelval绘制文字并改变颜色效果,用于检查字符渲染与颜色变化是否正常。dma_display->fillScreen():依次将屏幕填充为黑、红、绿、蓝、白等纯色,便于观察整屏刷新与颜色显示效果。delay(2000):每个显示步骤停留 2 秒,方便肉眼确认画面是否正确。dma_display->clearScreen():在测试结束后清空画面,避免上一帧内容残留。
void loop() {
// animate by going through the colour wheel for the first two lines
drawText(wheelval);
wheelval +=1;
delay(2000);
dma_display->clearScreen();
dma_display->fillScreen(myBLACK);
delay(2000);
dma_display->fillScreen( myRED);
delay(2000);
dma_display->fillScreen(myGREEN);
delay(2000);
dma_display->fillScreen(myBLUE);
delay(2000);
dma_display->fillScreen(myWHITE);
delay(2000);
dma_display->clearScreen();
}
【运行效果】

02_PatternPlasma
【代码分析】
loop():通过逐像素计算与调色板映射生成动态等离子特效。for (int x ...)/for (int y ...):遍历整块面板的每个像素,逐点计算并绘制颜色。sin8()、sin16()、cos16():结合坐标和time_counter生成动态变化的中间值v,构造流动波纹效果。ColorFromPalette(currentPalette, (v >> 8)):根据计算结果从当前调色板中取色。dma_display->drawPixelRGB888():将计算出的 RGB 颜色写入当前像素。time_counter、cycles、fps:分别用于驱动动画变化、统计循环次数和计算绘制帧率。if (cycles >= 1024):重置计数器并随机切换调色板,实现不同色系自动轮换。Serial.printf_P():每 5 秒输出一次Effect fps,用于观察特效绘制速度。
void loop() {
for (int x = 0; x < PANE_WIDTH; x++) {
for (int y = 0; y < PANE_HEIGHT; y++) {
int16_t v = 128;
uint8_t wibble = sin8(time_counter);
v += sin16(x * wibble * 3 + time_counter);
v += cos16(y * (128 - wibble) + time_counter);
v += sin16(y * x * cos8(-time_counter) / 8);
currentColor = ColorFromPalette(currentPalette, (v >> 8)); //, brightness, currentBlendType);
dma_display->drawPixelRGB888(x, y, currentColor.r, currentColor.g, currentColor.b);
}
}
++time_counter;
++cycles;
++fps;
if (cycles >= 1024) {
time_counter = 0;
cycles = 0;
currentPalette = palettes[random(0,sizeof(palettes)/sizeof(palettes[0]))];
}
// print FPS rate every 5 seconds
// Note: this is NOT a matrix refresh rate, it's the number of data frames being drawn to the DMA buffer per second
if (fps_timer + 5000 < millis()){
Serial.printf_P(PSTR("Effect fps: %d\n"), fps/5);
fps_timer = millis();
fps = 0;
}
} // end loop
【运行效果】

03_DoubleBuffer
【代码分析】
loop():演示双缓冲动画的完整流程,包括切换缓冲区、后台绘制和更新运动状态。display->flipDMABuffer():将后续绘图切换到未显示的后台缓冲区,用于减少动态图形闪烁。delay(1000/display->calculated_refresh_rate):等待当前帧完成显示,避免过早翻转缓冲区导致撕裂或闪烁。display->clearScreen():清空后台缓冲区,为新一帧绘制做准备。delay(25):模拟耗时绘图过程,用于更直观地观察启用双缓冲后的平滑效果。display->fillRect():绘制多个运动方块,形成动态测试画面。velocityx/velocityy判断逻辑:当方块碰到屏幕边界时反转运动方向,实现反弹效果。Squares[i].xpos/Squares[i].ypos更新:根据速度更新方块坐标,推动下一帧动画。
void loop()
{
// Flip all future drawPixel calls to write to the back buffer which is NOT being displayed.
display->flipDMABuffer();
// SUPER IMPORTANT: Wait at least long enough to ensure that a "frame" has been displayed on the LED Matrix Panel before the next flip!
delay(1000/display->calculated_refresh_rate);
// Now clear the back-buffer we are drawing to.
display->clearScreen();
// This is here to demonstrate flicker if double buffering is disabled. Emulates a long draw routine that would typically occur after a 'clearscreen'.
delay(25);
for (int i = 0; i < numSquares; i++)
{
// Draw rect and then calculate
display->fillRect(Squares[i].xpos, Squares[i].ypos, Squares[i].square_size, Squares[i].square_size, Squares[i].colour);
if (Squares[i].square_size + Squares[i].xpos >= display->width()) {
Squares[i].velocityx *= -1;
} else if (Squares[i].xpos <= 0) {
Squares[i].velocityx = abs (Squares[i].velocityx);
}
if (Squares[i].square_size + Squares[i].ypos >= display->height()) {
Squares[i].velocityy *= -1;
} else if (Squares[i].ypos <= 0) {
Squares[i].velocityy = abs (Squares[i].velocityy);
}
Squares[i].xpos += Squares[i].velocityx;
Squares[i].ypos += Squares[i].velocityy;
}
}
【运行效果】

04_OtherShiftDriverPanel
【代码分析】
loop():持续生成整屏动态特效,用于验证带移位寄存器驱动芯片面板的显示兼容性。for (int x ...)/for (int y ...):遍历整块屏幕,对每个像素分别计算颜色值。sin8()、sin16()、cos16():结合坐标和time_counter生成连续变化的颜色索引。ColorFromPalette(currentPalette, (v >> 8) + 127):根据中间值从调色板中映射出最终颜色。dma_display->drawPixelRGB888():将 RGB 颜色逐点写入 DMA 显示缓冲区。time_counter、cycles、fps:分别用于驱动动画变化、控制换色周期和统计绘制帧率。if (cycles >= 1024):定期重置动画参数并随机切换调色板,便于观察不同色彩下的显示效果。Serial.printf_P():周期输出 FPS 信息,用于评估当前图案绘制速度。
void loop(){
for (int x = 0; x < dma_display->width(); x++) {
for (int y = 0; y < dma_display->height(); y++) {
int16_t v = 0;
uint8_t wibble = sin8(time_counter);
v += sin16(x * wibble * 3 + time_counter);
v += cos16(y * (128 - wibble) + time_counter);
v += sin16(y * x * cos8(-time_counter) / 8);
currentColor = ColorFromPalette(currentPalette, (v >> 8) + 127); //, brightness, currentBlendType);
dma_display->drawPixelRGB888(x, y, currentColor.r, currentColor.g, currentColor.b);
}
}
++time_counter;
++cycles;
++fps;
if (cycles >= 1024) {
time_counter = 0;
cycles = 0;
currentPalette = palettes[random(0,sizeof(palettes)/sizeof(palettes[0]))];
}
// print FPS rate every 5 seconds
// Note: this is NOT a matrix refresh rate, it's the number of data frames being drawn to the DMA buffer per second
if (fps_timer + 5000 < millis()){
Serial.printf_P(PSTR("Effect fps: %d\n"), fps/5);
fps_timer = millis();
fps = 0;
}
}
【运行效果】

05_AnimatedGIFPanel_SD
【代码分析】
setup():完成 SD 卡、HUB75 面板和 GIF 解码器的初始化,为后续播放 GIF 动画做准备。SD_MMC.setPins():配置 SD 卡使用的时钟、命令和数据引脚。SD_MMC.begin("/sdcard", true):以 1-bit 模式挂载 SD 卡文件系统。SD_MMC.cardType()、cardSize()、totalBytes()、usedBytes():读取卡类型、容量和空间占用信息,用于确认存储介质状态。HUB75_I2S_CFG mxconfig(...):配置 HUB75 面板的宽度、高度和级联数量。dma_display = new MatrixPanel_I2S_DMA(mxconfig):创建 DMA 显示对象。dma_display->begin():启动 DMA 显示并分配显示缓冲区。SD_MMC.open("/gifs"):打开 GIF 文件目录。root.openNextFile():遍历/gifs目录中的文件。GifFiles.push_back(filename):将找到的 GIF 文件路径保存到列表中,供后续循环播放使用。gif.begin(LITTLE_ENDIAN_PIXELS):初始化 GIF 解码器,并设置像素字节序。
void setup()
{
Serial.begin(115200);
// **************************** Setup SD Card access via SD_MMC 1-bit ****************************
if (!SD_MMC.setPins(BSP_SD_CLK, BSP_SD_CMD, BSP_SD_D0)) {
Serial.println("SD_MMC setPins Failed");
return;
}
if(!SD_MMC.begin("/sdcard", true)){
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD_MMC.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD card attached");
return;
}
Serial.print("SD Card Type: ");
if(cardType == CARD_MMC){
Serial.println("MMC");
} else if(cardType == CARD_SD){
Serial.println("SDSC");
} else if(cardType == CARD_SDHC){
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
//listDir(SD_MMC, "/", 1, false);
Serial.printf("Total space: %lluMB\n", SD_MMC.totalBytes() / (1024 * 1024));
Serial.printf("Used space: %lluMB\n", SD_MMC.usedBytes() / (1024 * 1024));
// **************************** Setup DMA Matrix ****************************
HUB75_I2S_CFG mxconfig(
PANEL_RES_X, // module width
PANEL_RES_Y, // module height
PANEL_CHAIN // Chain length
);
// Keep ESP32-S3 default HUB75 mapping to avoid Flash/PSRAM reserved pins.
//mxconfig.clkphase = false;
//mxconfig.driver = HUB75_I2S_CFG::FM6126A;
// Display Setup
dma_display = new MatrixPanel_I2S_DMA(mxconfig);
// Allocate memory and start DMA display
if( not dma_display->begin() )
Serial.println("****** !KABOOM! HUB75 memory allocation failed ***********");
dma_display->setBrightness8(128); //0-255
dma_display->clearScreen();
// **************************** Setup Sketch ****************************
Serial.println("Starting AnimatedGIFs Sketch");
// SD CARD STOPS WORKING WITH DMA DISPLAY ENABLED>...
File root = SD_MMC.open("/gifs");
if(!root){
Serial.println("Failed to open directory");
return;
}
File file = root.openNextFile();
while(file){
if(!file.isDirectory())
{
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
std::string filename = "/gifs/" + std::string(file.name());
Serial.println(filename.c_str());
GifFiles.push_back( filename );
// Serial.println("Adding to gif list:" + String(filename));
totalFiles++;
}
file = root.openNextFile();
}
file.close();
Serial.printf("Found %d GIFs to play.", totalFiles);
//totalFiles = getGifInventory("/gifs");
// This is important - Set the right endianness.
gif.begin(LITTLE_ENDIAN_PIXELS);
}
【运行效果】

06_BitmapIcons
【代码分析】
setup():完成显示初始化,并通过渐变方式绘制 WiFi 图标,验证位图资源与绘制接口是否正常。dma_display->begin():启动 HUB75 面板显示。dma_display->setBrightness8(90):设置面板亮度。dma_display->fillScreen()/dma_display->clearScreen():初始化和切换阶段清空画面,避免残影干扰。for (int r = 0; r < 255; r++):逐步增加红色分量,用于生成淡入动画。drawXbm565(0,0,64,32, wifi_image1bit, ...):将 WiFi 的 XBM 位图绘制到屏幕上。loop():循环切换不同图标,实现图标轮播显示。drawXbm565(5,0, 32, 32, icon_bits[current_icon]):绘制当前索引对应的图标数据。icon_name[current_icon]:通过串口输出当前显示的图标名称,便于调试。current_icon = (current_icon + 1) % num_icons:循环更新图标索引,确保轮播不会越界。
void setup() {
// put your setup code here, to run once:
delay(1000); Serial.begin(115200); delay(200);
/************** DISPLAY **************/
Sprintln("...Starting Display");
dma_display = new MatrixPanel_I2S_DMA(mxconfig);
dma_display->begin();
dma_display->setBrightness8(90); //0-255
dma_display->clearScreen();
dma_display->fillScreen(dma_display->color444(0, 0, 0));
// Fade a Red Wifi Logo In
for (int r=0; r < 255; r++ )
{
drawXbm565(0,0,64,32, wifi_image1bit, dma_display->color565(r,0,0));
delay(10);
}
delay(2000);
dma_display->clearScreen();
}
void loop() {
// Loop through Weather Icons
Serial.print("Showing icon ");
Serial.println(icon_name[current_icon]);
drawXbm565(5,0, 32, 32, icon_bits[current_icon]);
current_icon = (current_icon +1 ) % num_icons;
delay(2000);
dma_display->clearScreen();
}
【运行效果】

07_Pixel_Mapping_Test
【代码分析】
loop():按行列顺序逐点点亮像素,用于检查面板像素映射与软件配置是否一致。for (int i ...)/for (int j ...):遍历FourScanPanel的全部像素坐标。FourScanPanel->drawPixel(j, i, FourScanPanel->color565(255, 0, 0)):将当前像素点亮为红色,形成可观察的扫描轨迹。delay(30):让扫描点以较慢速度移动,便于观察实际点亮顺序。dma_display->clearScreen():整屏扫描完成后清空画面,开始下一轮测试。delay(2000):在每轮扫描结束后停留 2 秒,方便确认映射结果是否正确。
void loop() {
for (int i = 0; i < FourScanPanel->height(); i++)
{
for (int j = 0; j < FourScanPanel->width(); j++)
{
FourScanPanel->drawPixel(j, i, FourScanPanel->color565(255, 0, 0));
delay(30);
}
}
delay(2000);
dma_display->clearScreen();
} // end loop
【运行效果】
