1. 项目整体设计与思路拆解
1.1 核心需求解析:为什么要单独聊 PSRAM 和 Flash
我最早接触 ESP32 的时候,其实跟大多数人一样,拿它当 Arduino 的高级替代品来用。点个灯、读个传感器、连个 WiFi,感觉这东西真香。但等项目做到一定程度——比如跑 LVGL 图形界面、接摄像头、做音频播放——问题立刻就来了:内存不够用,程序动不动就重启,Flash 分区也老是搞不明白。这时候才意识到,想用好 ESP32,光会调库是不够的,必须把 PSRAM 和 Flash 这两个存储家伙彻底吃透。
先说结论:Flash 是 ESP32 的“硬盘”,PSRAM 是 ESP32 的“内存条”。一台上位机电脑没有硬盘装不了系统,没有内存跑不了大程序,ESP32 也是同样道理。Flash 负责存放固件、文件系统、OTA 升级包、证书、配置参数等静态数据;PSRAM 负责在运行时给程序提供额外的大块内存,让复杂的应用能跑得起来。两者都是通过 SPI 总线挂在芯片外面的,所以常常被放在一起讨论。
很多新手容易搞混的一点是:ESP32 芯片内部其实已经有一定量的 SRAM 和 Flash 了(比如经典款 ESP32 有 520KB SRAM,部分模组内置 4MB Flash),那为什么还要外挂 PSRAM 和更大的 Flash?答案很简单:默认配置只能跑“入门级”应用,想要稍微上点规模,就必须学会使用外部存储。
关键字说透了:ESP32 的 Flash 通常从 4MB 起步,PSRAM 常见 2MB、4MB、8MB。你买的模组(比如 ESP32-WROOM-32、ESP32-S3-WROOM-1)本质上就是在芯片外面焊了一个 Flash 和一个可选的 PSRAM,封装成一个方便使用的整体。理解了这个基本架构,后面的所有操作——无论是烧录、分区、OTA 还是内存优化——就都串起来了。
1.2 方案选型背后的考量:为什么 ESP32 要把 Flash 和 PSRAM 外置
如果你拆过 ESP32 的模组或者看过原理图,会发现一个有意思的现象:本来 SoC 里已经可以集成存储,但乐鑫(Espressif)偏偏要把它做成外置 SPI Flash + SPI PSRAM 的架构。这其实是一个非常务实的设计决策。
外置 Flash 的最大好处是容量灵活 + 成本可控。手机存储芯片动辄要求高带宽、高耐用性,而 ESP32 作为物联网设备的主控,对存储带宽要求没那么苛刻,但对成本和体积非常敏感。SPI NOR Flash 价格便宜、接口简单、引脚占用少(通常 6 根线搞定),而且市面上从 1MB 到 64MB 的颗粒随便挑,同一个 PCB 设计可以只需要更换 Flash 颗粒就实现产品线的容量差异化,这对硬件选型太友好了。
PSRAM 外置则是另一套逻辑。ESP32 内置的 SRAM(静态随机存取存储器)速度虽快,但容量做不大——芯片的面积和功耗都受不了。而 PSRAM(伪静态随机存取存储器)本质上是 DRAM 加上自刷新电路,对外接口表现得像 SRAM,不需要像传统 DRAM 那样频繁刷新管理,所以成本低、容量可以做到比较大。Espressif 从 ESP32 初代开始就支持外挂 PSRAM,到了 ESP32-S3 更是推出了 Octal PSRAM(八线总线),带宽翻倍,跑 LVGL 这种图形框架才真正流畅起来。
2. PSRAM 深潜:原理、启用与实战应用
2.1 PSRAM 到底是什么,和 SRAM 有啥区别
先说一句大白话:PSRAM 就是为了让 ESP32 “内存不够用”这个问题得到解决而存在的。它名字里带个“Pseudo”(伪),是因为它的内部存储单元其实是 DRAM 的架构,需要用电容来存储电荷,电荷会泄漏,所以要周期性刷新。但厂家把刷新逻辑封装在芯片内部,对外呈现出普通 SRAM 的接口时序,所以你在用的时候完全不用操心刷新的事情,当成普通 RAM 用就行。
PSRAM 和 SRAM 的核心区别有这么几点:
| 维度 | SRAM | PSRAM |
|---|---|---|
| 存储单元结构 | 触发器(6T/8T) | 电容 + 晶体管(1T1C) |
| 是否需要刷新 | 不需要 | 内部自动刷新,对外不可见 |
| 单位容量成本 | 高 | 低 |
| 速度 | 快(纳秒级) | 稍慢(有刷新开销) |
| 典型容量 | 几百 KB | 2MB-16MB |
| 接口 | 并行为主 | SPI(串行)为主 |
在 ESP32 上,一般把内置 SRAM 称为 Internal SRAM,外部 PSRAM 称为 External RAM 或 SPIRAM。两者的性能差异真实存在:内置 SRAM 的访问速度更快,适合放实时性要求高的任务栈、中断处理等关键内存;PSRAM 速度慢一些,而且要走 SPI 总线,所以适合放数据缓冲区、帧缓冲(framebuffer)、音频样本等“容量大但对延迟不敏感”的数据。
我在实际项目里的习惯是:重要的、高频访问的变量放内部 SRAM,大块的数据缓冲(比如 LVGL 的 draw buffer、摄像头帧缓存、音频 I2S DMA 缓冲)放 PSRAM。这样既保证了关键路径的性能,又能让总可用内存大好几倍。
2.2 如何在 ESP-IDF 里启用 PSRAM
我用的是 ESP-IDF,这里面启用 PSRAM 非常简单,但也非常容易踩坑。先看 menuconfig 里的核心配置项:
idf.py menuconfig进入Component config → ESP32S3-specific → Support for external, SPI-connected RAM(不同芯片型号路径略有差异,ESP32 系列对应ESP32-specific,ESP32-S3 对应ESP32-S3-specific)。打开这个选项后,通常还需要关注以下几个配置:
- SPI RAM config → Mode (QUAD/OCT): ESP32-S3 要选 OCT(Octal),因为大多数 S3 模组外挂的是 Octal PSRAM;老款 ESP32 则选 QUAD。
- SPI RAM config → Frequency: 通常是 40MHz 或 80MHz,具体看模组的 PSRAM 颗粒规格。
- SPI RAM config → Initialization: 一般选
Initialize SPI RAM when the system starts,这样开机后就自动启用。 - SPI RAM config → Test: 可以勾选上运行内存测试,用于确认 PSRAM 正常工作。
配置完成后,代码里怎么用 PSRAM?最直接的方式是用heap_caps_malloc指定从 External RAM 分配:
// 从 PSRAM 分配 100KB 内存 uint8_t *buf = heap_caps_malloc(100 * 1024, MALLOC_CAP_SPIRAM); if (buf == NULL) { ESP_LOGE("MAIN", "PSRAM 分配失败"); return ESP_FAIL; } // 释放 free(buf);如果你希望某些库默认使用 PSRAM,比如 LVGL 的缓冲区,可以用环境变量或 SDK 配置来指定。在 LVGL 的移植代码里,常见做法是:
static lv_disp_draw_buf_t draw_buf; // 注意:这里优先从 PSRAM 分配 lv_color_t *buf1 = heap_caps_malloc(480 * 50 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM); lv_color_t *buf2 = heap_caps_malloc(480 * 50 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM); lv_disp_draw_buf_init(&draw_buf, buf1, buf2, 480 * 50);为什么推荐把 LVGL 的缓冲放 PSRAM?因为图形界面动辄几十 KB 到几百 KB 的缓冲区,放内置 SRAM 会让系统内存瞬间告急。而 LVGL 的刷新是周期性、区域性的,对延迟的敏感程度没那么极端,用 PSRAM 完全够用,实测下来流畅度差别很小。
2.3 PSRAM 典型应用场景:摄像头、LVGL、音频一个都跑不掉
摄像头场景是最典型的 PSRAM 刚需。ESP32-CAM 或者 ESP32-S3 接 OV2640、OV5640 摄像头时,拍一张 800x600 的 JPEG 图大概需要 30-60KB,拍 VGA 分辨率的原始 RGB565 图像更是要 6404802 = 614KB,这数据量放在内部 SRAM 里直接爆炸。所以 esp32-camera 库在初始化传感的时候,内部就默认把帧缓冲分配到了 PSRAM:
camera_config_t config; config.pixel_format = PIXFORMAT_JPEG; config.frame_size = FRAMESIZE_VGA; config.fb_location = CAMERA_FB_IN_PSRAM; // 关键:帧缓冲放 PSRAM config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;LVGL 图形界面也是 PSRAM 的忠实用户。一个 320x240 分辨率、16 位色的全屏 framebuffer 约 150KB,如果做双缓冲就要 300KB,而 ESP32 内置 SRAM 总可用量也就 300KB 出头,不做 PSRAM 方案的话 UI 根本跑不动。我用 ESP32-S3 + 4MB PSRAM 跑 480x480 的圆形屏幕 LVGL 界面,UI 的资源、字体缓存、图像解码缓冲全部可以放心丢到 PSRAM 里,系统依然能稳定运行。
音频播放是另一个容易被忽视的场景。ESP32 播放 MP3 或 AAC 时,解码器需要较大的工作缓冲区,I2S DMA 也需要环形缓冲。把音频流缓冲和 DMA buffer 放 PSRAM 后,解码卡顿的概率会明显下降。不过有一点要特别注意:I2S DMA 访问 PSRAM 时可能会有延迟峰值,所以 DMA 描述符链别全放 PSRAM,关键部分留在内部 SRAM 更保险。
3. Flash 深潜:存储架构、分区表与 OTA
3.1 Flash 在 ESP32 中的职责与类型
ESP32 用的 Flash 基本清一色是SPI NOR Flash,和电脑上的 SSD(NAND Flash)类型不同。NOR Flash 的优点是随机读取快、支持 XIP(就地执行),缺点是写入速度慢、写入前要先擦除,且擦除粒度大(通常 4KB 一扇区)。但对 ESP32 这种跑固件的芯片来说足够了——固件就是只读代码,运行时不需要频繁改写 Flash。
一句话总结 NOR 和 NAND 的区别:NOR 像一本“字典”,可以翻到任意一页直接读;NAND 像一本“笔记本”,写起来按块写,但读起来不能随意跳到任何一个字节。所以嵌入式设备的固件存储用 NOR,大容量数据存储(比如 U 盘、SD 卡)用 NAND。
在 ESP32 的体系里,Flash 承担了以下几个核心任务:
- 存储固件本身(Factory App、OTA App)
- 存储 NVS(Non-Volatile Storage),即非易失性存储,用于保存 Wi-Fi 配置、设备参数
- 存储 SPIFFS 或 LittleFS 文件系统(网页资源、图标、配置文件)
- 存储 OTA 的临时升级包
- 存储启动引导程序 bootloader
3.2 分区表详解:一张表看懂 Flash 布局
分区表是 ESP32 Flash 管理的灵魂。没有概念的人总觉得分区表无关紧要,实际上分区表搞错了,轻则 OTA 无法升级,重则固件直接起不来报flash download failed。
分区表在项目里的文件一般是partitions.csv,内容长这样:
# Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x200000, ota_0, app, ota_0, 0x210000, 0x200000, ota_1, app, ota_1, 0x410000, 0x200000,上面这个分区表对应的是4MB Flash的经典布局:factory 应用占 2MB,OTA_0 和 OTA_1 各占 2MB,加在一起其实是超过了 4MB 的(约 4MB + 64KB 左右)。实际上这种写法能成立的原因在于:factory 和 OTA 分区虽然都划了 2MB,但同一时间只会存在其中一份完整的固件——烧录 factory 时 ota_0/ota_1 是空的,做 OTA 升级时固件先写到 OTA_0,然后 bootloader 切换到 OTA_0 启动,下一版固件再写到 OTA_1。所以两者严格来说是“二选一”的关系,但物理空间上需要各占一个坑。
这里有个关键点:分区表必须和实际 Flash 容量完全匹配。如果你手里的模组是 4MB Flash,却用了 8MB 的分区表,烧录没问题,但跑到后面就会出错;反之,如果你实际有 8MB 却只用 4MB 分区表,那就是白白浪费硬件资源。
3.3 Flash 烧录的流程与失败排查
热搜词里反复出现flash download failed - target dll has been cancelled这个报错,我几乎每个月初都能看到群友截图求助。这个报错的意思是:烧录工具能识别到串口,但芯片没有正确进入下载模式,或者烧录过程中芯片复位/断电导致连接中断。最简单的几个排查点:
- 确认 ESP32 的 IO0(BOOT)引脚是拉低的,再按一下 RST 引脚,让芯片重新进入下载模式。
- 确认串口驱动是真的装好了,Windows 上常见的是 CP210x 驱动或 CH340 驱动,设备管理器里应当能看到对应的 COM 口。
- 检查串口波特率,有时候把烧录波特率从 921600 降到 460800 或 230400 能解决线材质量差导致的烧录失败。
- 如果用的是自动下载电路(如常见的基于 CH340 + 三极管的自动下载电路),部分模组需要确保 EN 和 IO0 之间有正确的 RC 延时网络,不然上电时序一错,下载模式就进不去。
然后说说烧录方式。ESP32 的烧录主要有三种:串口烧录(UART Download)、USB-OTG 烧录(ESP32-S3)、JTAG 烧录。串口烧录最通用,idf.py flash或者 Arduino IDE 上传都是走这条路。ESP32-S3 支持原生 USB 烧录,拔掉串口转换芯片,直接用 USB 线连电脑就能烧,但要注意用 USB 烧录时也需要进入 download 模式(按住 BOOT 再插线)。
3.4 OTA 升级:如何让设备在线更新固件
OTA(Over-The-Air)升级是物联网产品最基本的能力。ESP32 做 OTA 的核心思路是“影子分区”机制:当前运行 A 分区,新固件下载到 B 分区,校验通过后写入 otadata 分区标记下次从 B 启动,然后重启完成切换。
用一个最简单但可靠的 OTA 流程做例子:
esp_ota_handle_t ota_handle; const esp_partition_t *update_partition = esp_ota_get_next_update_partition(NULL); esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &ota_handle); // 循环从 HTTP/HTTPS 读取固件数据,写入 OTA 分区 // esp_ota_write(ota_handle, buf, len); esp_ota_end(ota_handle); esp_ota_set_boot_partition(update_partition); esp_restart();这段代码背后有两个容易踩的坑。
第一,OTA 下载过程中断电了怎么办?答案是不用慌,因为 APP 版本没有更新,重启后 bootloader 依然会从旧分区启动。但要注意下载过程中的“脏数据”会占用 OTA 分区的空间,所以建议 OTA 分区只保留两个,别分出太多 ota_x。
第二,分区表大小估算错误。如果你编译出的固件是 1.8MB,但 OTA 分区只有 1.5MB,那么 OTA 写入到一半就会返回错误。所以在绘制分区表之前,先idf.py size看一下实际固件大小,再留出至少 30% 的余量给未来功能扩展。
4. 实操流程:从硬件选型到代码配置全记录
4.1 硬件选型:WROOM、WROVER、MINI 怎么选
聊到 PSRAM 和 Flash 就绕不开模组选型。ESP32 的模组后缀是硬指标:
- ESP32-WROOM-32:经典款,内置 SRAM 520KB,通常外挂 4MB Flash,没有 PSRAM。适合做普通传感器节点、简单 TCP/IP 应用。
- ESP32-WROVER:在 WROOM 基础上增加了 8MB PSRAM(早期是 4MB),适合需要跑 LVGL、摄像头、音频的项目。
- ESP32-S3-WROOM-1:新一代产品,有 2MB PSRAM 和 8MB Flash 的组合,也有 4MB PSRAM + 4MB Flash 等版本,且 PSRAM 是 Octal 模式,带宽大幅提升。
- ESP32-S3-WROOM-1-N16R8:这里的 N16 表示 16MB Flash,R8 表示 8MB Octal PSRAM。这种配置跑大型 UI 项目或者做边缘计算推理都很安逸。
我个人的建议是:如果你不确定项目以后要做什么,直接买带 8MB PSRAM 的 WROVER 或 S3 模组,避免后面想加功能却发现内存不够用的尴尬。价格差距也就几块钱,但省下来的是几天的移植时间。
4.2 Flash 容量怎么定:固件大小估算与未来预留
选择 Flash 容量时,可以参考这个简单的公式:
Flash 需要容量 ≈ bootloader(约 32KB) + 分区表(4KB 或 8KB) + NVS(16KB-64KB) + 固件大小 × 2(如果支持 OTA 双分区) + 文件系统/证书/资源(50KB-5MB 不等)举例来说,如果你的固件编译出来是 1.5MB,需要支持 OTA,UI 资源文件系统要 2MB,那么推荐的最小 Flash 容量是:
0.5MB(引导 + NVS + 分区表) + 1.5MB × 2(APP 双分区) + 2MB(文件系统) = 大约 5.5MB这个量级用 8MB Flash 模组比较合适。市面上常见的节点设备固件只有 300-500KB,那 4MB Flash 绰绰有余。总之别把 Flash 卡得太死,固件体积一定会随着产品迭代快速膨胀。
4.3 从零配置一个新项目:ESP-IDF 与 Arduino 双视角
ESP-IDF 视角(推荐在生产项目中使用):
# 创建项目 idf.py create-project my_esp32_cam # 进入配置 cd my_esp32_cam idf.py menuconfig设置中需要刻意检查:
Serial flasher config → Default flash frequency:模组标称支持 80MHz(Quad)或 120MHz(Octal)就选对应值。Serial flasher config → Flash size:务必和模组实际容量一致,比如 8MB。Partition table → Partition table:选择Custom partition table CSV并指定自己的 partitions.csv。Component config → ESP32S3-specific → Support for external, SPI-connected RAM:如果模组带 PSRAM 就开启。
Arduino 视角(适合快速验证):
在 Arduino IDE 里选板子时,注意板型后缀,比如ESP32 Dev Module和ESP32 Wrover Module对应不同的内存配置。在Tools → Partition Scheme中可选的方案通常有:
Default 4MB with spiffsHuge APP (3MB No OTA)Minimal SPIFFS (1.9MB APP with OTA)No OTA (Large APP)
Arduino 的默认设置不一定开了 PSRAM。如果你用的是带 PSRAM 的板子,还需要在Tools → PSRAM里选择Enabled,然后在代码开头查一下:
#include "esp_heap_caps.h" void setup() { Serial.begin(115200); if (psramFound()) { Serial.printf("Total PSRAM: %d bytes\n", ESP.getPsramSize()); Serial.printf("Free PSRAM: %d bytes\n", ESP.getFreePsram()); } else { Serial.println("PSRAM not found!"); } }如果板子带 PSRAM 但psramFound()返回 false,90% 的原因是 Arduino IDE 板型设置选错了,或者 PSRAM 选项没打开。
4.4 实测:一个带 LVGL 界面的 ESP32-S3 项目内存分配记录
这是我最近做的一个 480x480 圆屏温湿度监控器项目的真实内存分配,给大家一个参考:
| 模块 | 分配位置 | 大小 |
|---|---|---|
| LVGL draw buffer ×2 | PSRAM | 480 × 60 × 2 = 57.6KB × 2 |
| LVGL 字体缓存 | PSRAM | 约 200KB |
| PNG 图像解码缓冲 | PSRAM | 约 150KB |
| Wi-Fi 任务栈 | 内部 SRAM | 4KB |
| 温度传感器读取缓冲 | 内部 SRAM | 2KB |
| MQTT 客户端缓冲 | 内部 SRAM | 8KB |
| 系统日志缓冲 | 内部 SRAM | 8KB |
运行起来后,内部 SRAM 空闲保持在 120KB 左右,PSRAM 空闲在 3MB 左右。如果当时我没启用 PSRAM,这个项目连 LVGL 界面都跑不起来——因为两个 draw buffer 加起来已经大于可用的内部 SRAM 了。
5. 常见问题与排查实录
5.1 烧录失败的排查思路
我遇到过太多次烧录失败的案例,这里整理成一个速查表:
| 报错信息 | 可能原因 | 处理办法 |
|---|---|---|
A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header | 芯片没进入下载模式 | 按住 BOOT 按键,再按一下 RST,松开 RST 后松开 BOOT |
error: flash download failed - target dll has been cancelled | 下载过程中连接断开,或芯片异常复位 | 检查供电电流是否足够(瞬时电流可能达 500mA),换一根短一点的 USB 线 |
Chip is ESP32-S3但选错芯片型号 | 板型选择错误 | 在工具/IDE 里明确选择 ESP32-S3 |
烧录到 50% 报WRITE ERROR | Flash 写入异常,可能 Flash 颗粒损坏或电压不稳 | 降低波特率再试,如果不能复现则考虑更换模组 |
| 烧录成功但重启后没有任何输出 | bootloader 损坏或 flash 容量设置不对 | 用esptool.py erase_flash擦除整个 Flash 后重新烧录 |
实操心得:遇到烧录失败,第一个动作永远不是换线、换波特率,而是先按一下 RST 键,听一下 CP2102 或者 CH340 的串口有没有重新枚举的声音。然后用串口监视器看 ESP32 的启动日志(按住 BOOT 进入下载模式时会有waiting for download的提示)。这些信息能直接告诉你芯片到底有没有活着、有没有进入下载模式。
5.2 PSRAM 启用后频繁重启或随机崩溃
这是一个非常典型的问题:明明 PSRAM 已经启用了,代码也编译过了,但运行几分钟后设备就崩溃重启。排查思路是这样:
第一步,检查电源。PSRAM 在访问瞬间会有电流尖峰,电源纹波太大直接导致 PSRAM 读写错误,进而触发 Guru Meditation Error(比如Cache disabled but cached memory region accessed)。给模组加一个 10-100μF 的电解电容和一个 0.1μF 的陶瓷电容在供电引脚上,能解决一半以上的问题。
第二步,检查 GPIO 冲突。ESP32 的 PSRAM 是用特定 GPIO 连接的,不同模组引出的引脚不同,如果你在代码里把和 PSRAM 复用的引脚当成了普通 GPIO 使用(比如 WROVER 的 PSRAM 用到了 GPIO16、GPIO17),就会导致总线冲突。查一下你模组的引脚定义,别乱占用这些专用引脚。
第三步,检查固件是否启用了 cache。PSRAM 必须通过 Cache 访问,如果配置成No cache模式或者外设频率过高导致 cache miss 校验出错,就会随机崩溃。ESP-IDF 中有一个CONFIG_SPIRAM_CACHE_WORKAROUND选项,老版本在部分场景需要开启,新版本一般自动处理了。
5.3 Flash 分区擦写寿命和损耗均衡
最后聊一个很多人不关心但很致命的问题:Flash 是有擦写寿命的,典型 SPI NOR Flash 的擦写次数是 10 万次左右。如果你的产品每秒钟往 NVS 里写一次计数器,那么大约 28 小时后 Flash 就报废了。
解决方案有两个方向。第一,降低写入频率:把多次写操作合并成一次,写之前先比较新旧值是否相同,相同就跳过。第二,使用损耗均衡的存储机制:ESP-IDF 的 NVS 本身具备一定的磨损均衡能力,但它是按扇区搬运的,最稳妥的做法是减少写入次数。
对于文件系统,NAND 通常建议使用 LittleFS 超过 SPIFFS,因为 LittleFS 的索引策略更适合嵌入式设备频繁断电的场景。SPIFFS 在掉电时有一定概率出现整个文件系统损坏,LittleFS 则通过 copy-on-write 和日志机制更好保护数据完整性。如果你的项目已经用了 SPIFFS 而且出现过文件损坏,可以考虑迁移到 LittleFS。
5.4 最后一个值得收藏的细节:擦除 Flash 的正确姿势
很多人烧录遇到诡异 bug(比如代码改了但运行起来还是旧逻辑、Wi-Fi 配置删不掉、能烧录但一直重启)时,第一反应是换代码,其实先做一件事更高效——把 Flash 彻底擦掉。
esptool.py --port COM10 erase_flash擦除后重新烧录 bootloader 和固件,90% 以上的“幽灵 bug”会消失。这是因为分区表或者 NVS 里残留了旧版本的配置数据,直接烧录不会覆盖它们,导致新固件启动时读到完全错乱的历史配置。
在 Arduino 场景下,擦除 Flash 的第一种方法是在工具菜单中选择Erase Flash再烧录;第二种方法是按住 BOOT 再插 USB 线,打开ESP32 Sketch Data Upload工具时会提示是否擦除。总之,记住这个操作,能帮你省下很多调试时间。
6. 写在最后:个人项目中的一点体会
做 ESP32 项目这几年,我最大的感受是:很多人不是被代码卡住的,而是被“内存容量和存储结构”卡住的。掌握 Flash 分区规划之后,OTA 升级再也没出过问题;掌握 PSRAM 分配之后,LVGL 界面、摄像头项目信手拈来。这两颗小小的 SPI 芯片,几乎决定了 ESP32 项目的天花板在哪里。
最后再分享一个小技巧:每次拿到一个新模组,先别急着写业务逻辑,先跑一遍idf.py monitor看启动日志,里面会清楚地打印 Flash 大小、PSRAM 大小、芯片版本、MAC 地址。这能帮你确认硬件和软件配置是否一致,避免后期一边做功能一边怀疑“为什么我的内存和文档对不上”。这个习惯坚持下来,你会在嵌入式开发的路上少踩很多无谓的坑。