ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ESP32-S3 多SPI并发翻车?3套引脚方案一次讲透

ESP32-S3 多SPI并发翻车?3套引脚方案一次讲透 ESP32-S3 多SPI并发翻车3套引脚方案一次讲透【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32刚上电的板子TFT 花屏还没持续三秒SD 卡就开始报文件系统坏块紧接着 brownout 重启。排查一周你大概会得出和我一样的结论ESP32-S3 多SPI并发的问题九成出在引脚接法和初始化代码上不在器件本身。默认引脚定义为何就是冲突源头冲突的起点就藏在板级定义文件里variants/esp32s3/pins_arduino.h给整块板子只划出了一组 SPI 物理引脚四根信号线全部固定static const uint8_t SS 10; static const uint8_t MOSI 11; static const uint8_t MISO 13; static const uint8_t SCK 12;图1HSPI VSPI 引脚分配时先对照开发板丝印避开 Flash/PSRAM 占用脚再叠加一个容易踩空的事实S3/S2/C3 上FSPI与HSPI是同一个 GPSPI 控制器的两个别名见libraries/SPI/src/SPI.cpp末尾的构造逻辑。你new SPIClass(HSPI)再new SPIClass(VSPI)只要两组 begin() 想挂两组不同的 SCK第二个挂载必然失败。也就是说一条总线可以挂 N 个片选但要同时跑必须真的有两组独立引脚。对比项单总线N 个 CS 共享 1 组引脚多总线每总线独立引脚组并发能力任意时刻只有 1 片在传其余等待每条总线各跑各的真正并行带宽分配设备越多单设备时延越大各占总线带宽互不挤占时钟策略全总线被迫取最低公倍数可按设备分别设 40 MHz / 20 MHz故障隔离一根线出错所有设备陪葬坏哪条总线只影响该组设备按设备数量选SPI策略三档策略先对号入座再往下读设备数量策略改动量1~2 片单总线 独立片选只改 CS 接线3~4 片双总线拆分引脚组物理分离重接 2 组 SCK/MOSI/MISO≥5 片硬件 SPI 带 2 片 其余 bit-bang增加软件 SPI 驱动1~2个设备单总线双片选最小改动两块芯片共用 SCK/MOSI/MISO各自独占一根 CS。切换 CS 就是切换设备硬件零成本改动量最小。#include SPI.h void setup() { SPI.begin(12, 13, 11, 10); pinMode(9, OUTPUT); // 第二片设备片选 digitalWrite(10, HIGH); digitalWrite(9, HIGH); } void loop() { SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); digitalWrite(10, LOW); SPI.transfer(0x42); digitalWrite(10, HIGH); SPI.endTransaction(); }⚠️ 这条总线上的时钟必须取最慢那片能吃的频率快屏跟着传感器降速是常态。3~4个设备HSPI与VSPI双总线拆分到了第三片就该拆线了。S3 用户侧的 SPI 控制器只有一个所以拆的不是控制器而是引脚组一组走默认脚另一组走任意空闲 GPIOS3 上 GPSPI 支持 GPIO 矩阵begin(sck, miso, mosi, ss)传参即可改道官方示例libraries/SPI/examples/SPI_Multiple_Buses/SPI_Multiple_Buses.ino就是这么干的。引脚分配一次到位别留以后再说总线SCKMOSIMISOCS(示例)建议时钟建议挂GPSPI-0默认组12111310 / 940 MHzSD 卡等存储GPSPI-1第二组4548472120 MHzTFT 屏、高速传感器下面这段完成两条总线的初始化并用 SPI 事务把 CS 拉低到拉高包起来保证多任务下没人半路插队#include SPI.h SPIClass *busA new SPIClass(FSPI); // 默认引脚组 SPIClass *busB new SPIClass(HSPI); // 别名同一 GPSPI走第二组引脚 void setup() { busA-begin(); // SCK12/MISO13/MOSI11 busB-begin(45, 47, 48, 21); // 自定义第二组引脚 pinMode(9, OUTPUT); digitalWrite(9, HIGH); pinMode(21, OUTPUT); digitalWrite(21, HIGH); } void sdRead(uint8_t *buf, size_t len) { busA-beginTransaction(SPISettings(40000000, MSBFIRST, SPI_MODE0)); digitalWrite(10, LOW); busA-transferBytes(nullptr, buf, len); digitalWrite(10, HIGH); busA-endTransaction(); } void tftWrite(uint8_t *buf, size_t len) { busB-beginTransaction(SPISettings(20000000, MSBFIRST, SPI_MODE3)); digitalWrite(21, LOW); busB-transfer(buf, len); digitalWrite(21, HIGH); busB-endTransaction(); }SPI 事务管理在这里不是仪式感beginTransaction/endTransaction之外直接调transfer()不会锁参数多任务里两个任务同时改时钟分频波形就花了。图2两条总线各出一路 SPI 波形的示波实录SPI 总线分离成功的直接证据≥5个设备硬件SPI带2片其余bit-bang第五条线接上来时别再找第三条硬件总线了S3 的高速 GPIO 控制器就这么多DMA 通道也是有限资源。现实做法是硬件 SPI 带最吃带宽的 2 片SD 卡 大屏剩余 3 片交给软件 SPI——bit-bang 不占 DMA 通道跟硬件总线真并行。传感器跑几百 kHz 绰绰有余。typedef struct { int sck, mosi, cs; } SPISw; SPISw sw { .sck 5, .mosi 6, .cs 7 }; uint8_t swTransfer(uint8_t out) { uint8_t in 0; for (int i 7; i 0; i--) { digitalWrite(sw.mosi, (out i) 1); digitalWrite(sw.sck, HIGH); in (in 1) | digitalRead(sw.misi); digitalWrite(sw.sck, LOW); } return in; } void swWrite(uint8_t *buf, size_t len) { digitalWrite(sw.cs, LOW); for (size_t i 0; i len; i) swTransfer(buf[i]); digitalWrite(sw.cs, HIGH); }DMA 通道的分配不用手动Arduino 的 SPI 驱动对 64 字节以上的批量transfer自动走 DMA小于 64 字节走 FIFO。所以 DMA 通道优化的要点不是抢通道而是把批量传输攒大——1 KB 一次搬运和 1 KB 拆 16 次 64 字节前者才进 DMACPU 占用差一个量级。验证与排障引脚分完了怎么确认没接错上电自检走一遍 5 步两个 begin() 都返回 true逻辑分析仪看第二路 SCK 有波形只拉低 CS-A确认 CS-B 无时钟模式与器件 datasheet 一致满速压 10 分钟复位计数为 0第 2 步最容易翻车很多人以为双总线生效了其实第二路根本没有时钟出来。压一压底用这段骨架跑一分钟错误率低于百万分之一才算过关void stressSPI(SPIClass *a, SPIClass *b) { uint32_t bad 0, n 1000000; for (uint32_t i 0; i n; i) { size_t len 4 (i 4095); a-beginTransaction(SPISettings(20000000, MSBFIRST, SPI_MODE0)); a-transferBytes(nullptr, nullptr, len); a-endTransaction(); b-beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE3)); b-transfer(0xA5); b-endTransaction(); if (i % 100000 0) { Serial.printf(%u bad%u\n, i, bad); } } Serial.printf(err rate: %.4f%%\n, bad * 100.0 / n); }两个高频故障现象-根因-修复直接抄故障一第二条总线 begin() 返回 false。现象总线一初始化就失败串口打出 Attaching pins to SPI failed。根因选的 GPIO 撞进 Flash/PSRAM 占用区或两个实例都往同一个控制器挂不同 SCK。修复换到空闲 GPIO确认两次 begin() 都返回 true。故障二双屏显示错乱疑似并发冲突。现象两屏互相串画面降速就好。根因第二总线根本没跑起来两屏实际共用一条物理线只是 CS 不同。修复把第二块屏的 SCK/MOSI/MISO 改接到第二组 GPIObegin 传参。说到底ESP32-S3 多SPI并发的解法很朴素一条总线只挂一组物理引脚芯片多了就拆线或 bit-bang并发能力由物理接线决定不靠运气。想抄作业的话从 libraries/SPI/examples/SPI_Multiple_Buses/ 开始改最快。【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表