
让一块芯片跑通两路设备arduino-esp32 中 ESP32-S3 双 SPI 总线配置完全指南【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32在基于 arduino-esp32 核心的 ESP32-S3 项目里TFT 屏和 SD 卡只要接在同一组 SPI 引脚上画面开始闪、文件开始损坏问题多半不在代码。根源是两路设备共用一组信号线互相抢占。这篇文章带你用双总线分离方案把两路 SPI 设备拆到两条独立总线上每一步都能直接照着做。快速结论方案就这 5 件事✅ ESP32-S3 有两条用户可用的硬件 SPI 总线FSPI 和 HSPI信号线彼此独立、可并行工作✅ 核心代码只有一行new SPIClass(FSPI)就能多出一条总线✅ 两条总线各配一套互不重叠的引脚和片选CS✅ 每个设备在beginTransaction里单独设置时钟、位序和模式✅ 项目里自带参考实现libraries/SPI/examples/SPI_Multiple_Buses/SPI_Multiple_Buses.ino可直接抄原理白话版为什么一条路会堵把一条 SPI 总线想象成单车道公路SCK 是红绿灯MOSI/MISO 是双向车道片选是收费站。两个设备接在同一条总线上等于两辆车抢一个红绿灯——屏幕要刷帧SD 卡要写扇区谁也等不了谁结果就是画面撕裂、文件系统报错。ESP32-S3 的好消息是它还有另外两条路FSPI对应手册里的 SPI2和 HSPI对应 SPI3各自有独立的时钟分频和 DMA 通道。在 arduino-esp32 的源码 cores/esp32/esp32-hal-spi.h 里这两条正是留给用户使用的#define FSPI 0 // ESP32C2/C3/C5/C6/H2/S2/S3/P4 - SPI 2 总线 #define HSPI 1 // ESP32S2, S3, P4 - SPI 3 总线就这两行宏定义了可用的总线编号。默认的SPI全局对象绑在 HSPI 上再new一个SPIClass(FSPI)第二条路就通了。 另外 S3 内部有 GPIO 矩阵这两条总线的信号几乎可以路由到任意 GPIO默认引脚被占光也能换。GPIO 矩阵允许 SPI 等外设信号映射到不同 GPIO这是灵活分配引脚的关键手把手实施第一步先查你这块板的默认引脚动手接线前先看板上默认 SPI 引脚定义。ESP32-S3 官方板在 variants/esp32s3/pins_arduino.h 里是这组static const uint8_t SS 10; // 片选 static const uint8_t MOSI 11; // 主出从入 static const uint8_t MISO 13; // 主入从出 static const uint8_t SCK 12; // 时钟这组引脚默认被SPI对象HSPI占用。如果第一个设备比如屏幕已经用了它们第二个设备就得另找一套。具体走哪几脚可对照板子的完整引脚布局图确认开发板引脚布局图标注了各 GPIO 位置方便规划 SPI 走线第二步给两条总线分配互不重叠的引脚项目示例里给 S3 用的是一套现成方案全部是 S3 有效 GPIO直接照抄即可总线SCKMISOMOSICS建议接的设备HSPI默认 SPI14121315TFT 屏FSPI新建实例1819235SD 卡原则只有一条两条总线各自的 4 根信号线和 CS 都不能互相重叠。第三步创建第二条总线实例这是最关键的一步。默认的SPI已经在 HSPI 上了我们只需再建一个 FSPI 实例#include SPI.h SPIClass *sdBus NULL; void setup() { // 默认 SPI 对象在 HSPI 上给屏幕 SPI.begin(14, 12, 13, 15); // SCK, MISO, MOSI, CS // 第二条独立总线给 SD 卡 sdBus new SPIClass(FSPI); sdBus-begin(18, 19, 23, 5); // API 不会自动拉低 CS需手动初始化为高电平 pinMode(15, OUTPUT); digitalWrite(15, HIGH); pinMode(5, OUTPUT); digitalWrite(5, HIGH); }这段代码做了三件事把两条总线分别映射到两套引脚再手动把两个 CS 置为输出高电平。最后一步容易漏——arduino-esp32 的 SPI API 不管片选得自己翻转。第四步各自事务互不干扰之后两个设备各用各的总线读写配置完全独立void loop() { // 屏幕20MHz、Mode 0 SPI.beginTransaction(SPISettings(20000000, MSBFIRST, SPI_MODE0)); digitalWrite(15, LOW); SPI.transfer(tftFrame, tftFrameLen); digitalWrite(15, HIGH); SPI.endTransaction(); // SD 卡10MHz、Mode 3 sdBus-beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE3)); digitalWrite(5, LOW); sdBus-transferBytes(sdCmd, sdCmdLen); digitalWrite(5, HIGH); sdBus-endTransaction(); }两段事务用的是不同对象、不同时钟、甚至不同模式却互不影响——这就是双总线分开的全部意义A 路的红绿灯不再需要等 B 路。第五步可选大流量交给 DMA连续大块读写时把数据放进 DMA 缓冲区一次性搬运比逐字节轮询稳定得多。SPISettings、transfer等接口细节都在 libraries/SPI/src/SPI.h 里按需查即可日常小数据包用上面的写法已经够。效果如何示例文件夹里附了一张逻辑分析仪截图能直观看到两组时钟与数据线并行工作、各自波形干净无串扰左侧三条 VSPI 线SCLK/MISO/MOSI正在传输的同时右侧 HSPI 三线也开始工作两路总线真正并行优化前后的定性对比参考值实际取决于你的器件指标改前双设备共一总线改后双总线分离屏幕刷新偶发撕裂、噪点稳定无异常SD 读写数据错乱、偶发文件系统错误正常总线吞吐两设备互相等待各拿满自身带宽代码改动量无仅增加一个 SPIClass 实例⚠️ 一句话结论双总线不会提升单条总线的物理上限但消除了设备间互相等待整体吞吐和稳定性是量级上的改善。踩坑实录设备识别不到SPI 无响应/ 原因CS 引脚初始没设成输出高电平从设备被误选中 / 处理setup 末尾补上pinMode(CS, OUTPUT); digitalWrite(CS, HIGH)。数据总差一位或全错/ 原因SPI 模式与器件要求不符Mode0 和 Mode3 混用 / 处理查器件手册改 SPISettings 第三个参数两条总线可以各用一个模式。屏幕正常SD 卡时钟一高就出错/ 原因SD 卡允许的最高频率低于屏幕 / 处理单独压低 SD 卡时钟10MHz 通常稳妥无需连带降屏幕。S3 上找不到 VSPI 宏/ 原因VSPI 只存在于老款 ESP32S3 上两条总线叫 FSPI 和 HSPI / 处理对照 cores/esp32/esp32-hal-spi.h示例文件里也做了#define VSPI FSPI的兼容别名。两条总线引脚打架/ 原因同一 GPIO 同时被两路信号占用 / 处理按第二步的表格严格分线拿不准时回板卡 variant 目录核对引脚表。延伸资源双总线示例含备选引脚宏与波形图libraries/SPI/examples/SPI_Multiple_Buses/总线编号与 SPI 模式宏定义cores/esp32/esp32-hal-spi.hSPIClass / SPISettings 接口声明libraries/SPI/src/SPI.hS3 板卡默认引脚定义variants/esp32s3/pins_arduino.h【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考