跳到主要内容

Arduino 开发

本章节包含以下部分,请按需阅读:

Arduino 入门教程

初次接触 Arduino ESP32 开发,想要快速上手?我们为您准备了一套通用的 入门教程

请注意:该教程使用 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_countercyclesfps:分别用于驱动动画变化、统计循环次数和计算绘制帧率。
  • 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_countercyclesfps:分别用于驱动动画变化、控制换色周期和统计绘制帧率。
  • 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

【运行效果】