ESP32-S3 有 8MB PSRAM 还是说内存不够之解决办法上一篇ESP32-S3 存的 Wi-Fi 密码自己就没了之解决办法本文代码来自开源仓库github.com/tangeai/xiaotai-esp32主工程lckfb-szpi-esp32s3-tirtc/。配置取自sdkconfig.defaultscmake 脚本有节选。板子上焊了 PSRAM编译时却报内部 RAM 不够跑起来分配大缓冲还是失败。这份工程其实已经开了CONFIG_SPIRAM_USE_MALLOC但要真正用上还得分两头自己写的代码要显式指定能力标志第三方库里写死的大缓冲得另外想办法。而有些内存这家工程是刻意留在内部的——摄像头 DMA 暂存、驱动栈和控制结构就是。这篇按这份工程的实际配置讲哪几个开关开着、哪些内存留在内部、以及两个把第三方库的大缓冲挪进 PSRAM的做法。一、硬件基线与几个关键开关这份工程的基线写在sdkconfig.defaults开头板型标注为 ESP32-S3 N16R8配置里能直接读到的是CONFIG_ESPTOOLPY_FLASHSIZE16MBPSRAM 的容量没有单独列出来按 N16R8 的命名习惯是 8 MB实际容量以硬件规格或启动日志为准。其他规格要同步调整。PSRAM 相关的几行是CONFIG_SPIRAMy CONFIG_SPIRAM_MODE_OCTy CONFIG_SPIRAM_TYPE_AUTOy CONFIG_SPIRAM_SPEED_80My CONFIG_SPIRAM_USE_MALLOCy CONFIG_SPIRAM_MEMTESTy CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL131072 # CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP is not set CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORYy CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEMy逐条说MODE_OCT是八线模式SPEED_80M是 80 MHz。这两个要和硬件匹配换板子第一件事就是核这两项。各项开关的完整语义要查 Kconfig 的帮助文本本文材料里没有。USE_MALLOC这份工程是开着的作用是让常规的malloc也能用上 PSRAM。关掉之后malloc具体怎么走材料里没有说明。MEMTEST打开之后启动时会做一遍 PSRAM 自检出问题早发现。自检打什么日志、覆盖多大范围要另行核验。RESERVE_INTERNAL131072也就是 128 KiB是给只认内部内存的地方保留的不让 PSRAM 并进来之后消耗掉这部分。TRY_ALLOCATE_WIFI_LWIP这份工程没有启用也就是没走让 Wi-Fi 和 LwIP 的缓冲也用 PSRAM这个选项。这不等于 Wi-Fi 和 LwIP 的所有分配都一定不落在 PSRAM 上材料里没有全部路径。后面两行打开允许 BSS 段放外部和允许任务栈放外部。外部 BSS 是后面 LVGL 那个方案的检查条件允许外部任务栈则和摄像头应用栈的配置有关。还有一行容易被忽略CONFIG_SPIRAM_XIP_FROM_PSRAMy上面那句注释解释了为什么开让双麦 AFE 的热代码和常量不要走较慢的 DIO Flash 总线启动时的代码副本放在 PSRAM 里而不是靠加大内部 cache。这一条只适用于 N16R8。二、这家工程把什么留在内部先说清楚范围下面这些是这份工程自己的取舍都写在配置注释里。至于别的代码、别的缓冲能不能放 PSRAM本文的材料不足以给出通用判据。这份工程留在内部的是摄像头的 DMA 暂存、驱动的栈和控制结构LVGL 那套适配也明确不动 ISR 控制和 SPI DMA 传输缓冲。Wi-Fi 的缓冲留在内部TX 保持动态分配# TX stays dynamic so PSRAM does not force sixteen 1.6 KiB static TX buffers. CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM4 CONFIG_ESP_WIFI_RX_BA_WIN4 CONFIG_ESP_WIFI_TX_BA_WIN6 CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM32 CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFERy CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM32注释的意思是TX 走动态分配免得额外分配出 16 个 1.6 KiB 的静态 TX 缓冲。Wi-Fi 的额外 IRAM 优化也关掉了# Extra IRAM optimization costs over 5 KiB of internal memory. Keep the current # budget for AFE/DMA; do not blindly use bulk-throughput buffer presets. # CONFIG_ESP_WIFI_EXTRA_IRAM_OPT is not set注释说得很明白那项优化要花掉 5 KB 以上的内部内存这份工程选择把预算留给 AFE 和 DMA。摄像头的取舍最典型下一节单独讲。三、摄像头DMA 暂存留内部完整帧放 PSRAMCONFIG_CAMERA_PSRAM_DMA在这份工程里是关掉的。注释给了原因# H5-only GC2145: 16 KiB internal DMA staging, complete frames in PSRAM. # Direct PSRAM DMA corrupted raw frames under the measured audio/Wi-Fi load. # Driver stack/control stay internal; the 16 KiB application stack is PSRAM. # Camera buffers exist only while viewing, not at boot. # CONFIG_CAMERA_PSRAM_DMA is not set CONFIG_CAMERA_TASK_STACK_SIZE4096 CONFIG_CAMERA_CORE1y CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX16384注释记录的内容是H5 那颗 GC2145 在实测的音频加 Wi-Fi 负载下直接使用 PSRAM 做 DMA 曾损坏原始帧因此这份工程关掉了CONFIG_CAMERA_PSRAM_DMA改成 DMA 暂存最多 16 KiB 放内部、完整帧放 PSRAM驱动的栈和控制结构留在内部16 KiB 的应用栈放 PSRAM。另外摄像头缓冲只在预览时存在开机不占。注意这条的限定条件它是 H5 那颗 GC2145 上的实测记录换sensor、换板子、负载不同结论可能不同。本文只是转述注释没有复现。应用栈那部分的配置依据是CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEMy这个开关允许外部任务栈注释记录该应用的 16 KiB 栈位于 PSRAM。任务具体怎么创建、还有什么使用限制材料里没展示。四、把第三方库的大缓冲挪进 PSRAM自己写的代码好办用heap_caps_malloc(..., MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)就行。第三方库的麻烦在于它的分配写在库里面。这份工程用了两个做法都写在cmake/下。做法一改编译期宏只搬 BSS 数组LVGL 的内存池是一个静态大数组。脚本没有去改它的 TLSF 分配器只是给lv_mem.c这一个源文件加了两个编译期宏set_property(SOURCE ${source} TARGET_DIRECTORY ${lvgl_lib} APPEND PROPERTY COMPILE_DEFINITIONS LV_MEM_POOL_INCLUDE\esp_attr.h\ LV_ATTRIBUTE_LARGE_RAM_ARRAYEXT_RAM_BSS_ATTR)效果是把那个数组的定义换成带EXT_RAM_BSS_ATTR的版本于是它落在 PSRAM 里。脚本开头的注释限定了范围# Keep LVGLs bounded TLSF pool and allocator semantics. Only its backing BSS # moves; esp_lvgl_ports ISR controls and SPI DMA transfer buffer are untouched.分配器的语义不变ISR 控制和 SPI DMA 传输缓冲都不动。这类改法最怕的是上游源码一变宏名或数组定义改了构建时不报错但宏没产生预期效果。脚本里做的是字符串存在性检查——先读源文件找不到预期的两行就直接报错foreach(anchor IN ITEMS #include LV_MEM_POOL_INCLUDE static LV_ATTRIBUTE_LARGE_RAM_ARRAY MEM_UNIT work_mem_int[LV_MEM_SIZE / sizeof(MEM_UNIT)];) string(FIND ${original} ${anchor} anchor_pos) if(anchor_pos EQUAL -1) message(FATAL_ERROR LVGL pool hooks changed; review ${source}) endif() endforeach()前置条件也写成了硬错误目标不是 S3、没开 PSRAM、或者没允许外部 BSS直接FATAL_ERROR。此外还检查了CONFIG_LV_MEM_CUSTOM和CONFIG_LV_MEM_ADDR两项配置改了同样报错。这两项前面没提这里补上。做法二改源码副本替换分配函数MQTT 的情况不一样。注释里的说法是 IDF 5.5.4 只暴露了 buffer 大小没有给 RX/TX 的分配器选择。这份工程选择的办法是生成一份构建目录内的副本把分配表达式换掉。节选列出了三个源文件和三组待替换的表达式set(old_allocations (uint8_t *)malloc(buffer_size) (uint8_t *)calloc(buffer_size, sizeof(uint8_t)) heap_caps_malloc(message-len message-remaining_len, MQTT_OUTBOX_MEMORY)) set(new_allocations (uint8_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT) (uint8_t *)heap_caps_calloc(buffer_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT) heap_caps_malloc(message-len message-remaining_len, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT))三个文件是mqtt_client.c、lib/mqtt_msg.c、lib/mqtt_outbox.c。按注释的说法改完的副本放在构建目录里上游目标的编译选项、包含路径和依赖都保持原样。具体的复制、写文件和替换源文件列表的实现本文引用的节选里没展示。同样有防上游变更的措施这次是校验源码哈希string(SHA256 source_hash ${original}) if(NOT source_hash STREQUAL expected_hash) message(FATAL_ERROR MQTT source changed: ${source}; review the PSRAM buffer adapter) endif()哈希对不上就停下来要求人工复核而不是拿旧的替换规则去套新代码。脚本顶部的注释把边界写得很清楚# IDF 5.5.4 exposes MQTT buffer sizes, but no RX/TX allocator selection. # Adapt only task-context payload allocations in build-local source copies. # Do not change the SDK installation, task lifecycle, locks or global malloc.只改任务上下文里的 payload 分配不动 SDK 安装、任务生命周期、锁和全局 malloc。注释还提到 NULL 检查和free()都保持上游不变内部不做兜底——也就是说分配失败就是失败不会悄悄退回内部 RAM。两种做法怎么选先看库本身有没有合适的配置项或宏。LVGL 这种能靠编译期宏解决的改动面小、不碰源码优先用。MQTT 这种写了malloc又没有分配器选项的这份工程选择改构建目录里的源码副本并加了哈希校验防止上游变了还继续套旧规则——这是这家工程的选择不是唯一做法别的库也可能有别的路子。注释里提到的另一条边界是不修改 SDK 安装目录。至于重装 IDF 或换机器有没有副作用超出了这条注释能证明的范围。五、自己写代码时怎么分配需要大缓冲时用能力分配器显式指定 PSRAMs_audio_rx_poolheap_caps_calloc(AUDIO_RX_QUEUE_DEPTH,sizeof(*s_audio_rx_pool),MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT);队列本身也能指定s_audio_rx_ready_queuexQueueCreateWithCaps(AUDIO_RX_QUEUE_DEPTH,sizeof(uint8_t),MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT);静态变量加个属性就行前面 LVGL 那套用的就是它staticEXT_RAM_BSS_ATTRwifi_manager_credentials_ts_station_credentials;这份工程里 NVS worker 的任务栈和 TCB 是显式声明成放内部 RAM 的staticDRAM_ATTR StaticTask_t s_tcb;staticDRAM_ATTR StackType_t s_stack[NVS_WORKER_STACK_BYTES/sizeof(StackType_t)];DRAM_ATTR的作用就是把数据放到内部 RAM。至于为什么这么放——是不是因为 Flash 写入期间的访问限制——源码注释里没有写理由本文不作断言。六、怎么确认有没有用上一个常用的办法是分别打印内部和 PSRAM 的堆信息比如用heap_caps_print_heap_info分别传MALLOC_CAP_INTERNAL和MALLOC_CAP_SPIRAM看两边的总量和剩余。这个 API 的输出字段和用法要查你用的 IDF 版本本文材料里没有它的调用。另外要留意堆的剩余量只反映动态分配的部分外部静态 BSS 和 XIP 占用不体现在里面所以不能靠PSRAM 剩余接近总量就断定没人用它。启动时开了CONFIG_SPIRAM_MEMTEST会做一遍自检。硬件没焊好或者模式、速度配错通常在这一步就暴露但具体打什么日志、容量报多少、失败后怎么处理要另行核验。分配返回 NULL 时先确认要的大小和用的能力标志。想明确要求从 PSRAM 分配就要像上面的示例那样指定MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT。自己跑一遍路径需要什么做什么浏览器烧固件立创实战派 ESP32-S3 V1.0.1/N16R8 USB 线不用装 ESP-IDFRelease 页下载*-full-16MB.binespressif.github.io/esptool-js地址0x0Flash 三项选keep看堆信息上面那块板 USB 线串口看启动时的 PSRAM 自检日志status命令看运行状态改配置自己编ESP-IDF 环境改sdkconfig.defaults后idf.py build对比堆信息变化本文的验证点是三处对照。第一处看启动时有没有 PSRAM 自检相关的日志以及它报告的容量和板型标称对一下。第二处是堆信息分别打印内部和 PSRAM 的剩余正常情况两边都有使用痕迹PSRAM 剩余等于总量只能说明动态分配没用到它不排除外部 BSS 已经占用。第三处是摄像头这份工程把CONFIG_CAMERA_PSRAM_DMA关掉了可以试着打开在音视频加 Wi-Fi 的负载下看画面验证注释里记录的那次损坏能不能复现。诚实限制摄像头 DMA 损坏帧是源码注释记录的实测限定在 H5 的 GC2145 上我没有在这块板上复现。板型 N16R8 是配置注释里的标注PSRAM 容量本文材料没有独立列出。LVGL 和 MQTT 两个脚本是这份工程自己的做法依赖特定版本的 LVGL 与 IDF 5.5.4LVGL 那套会在指定字符串或配置条件不符时报错MQTT 那套会在源码哈希与预期不符时报错但两者都不是全量校验上游在别处变动不一定被拦住。代码仓库github.com/tangeai/xiaotai-esp32路径lckfb-szpi-esp32s3-tirtc/sdkconfig.defaultsPSRAM 与摄像头配置、cmake/lvgl_psram_pool.cmake、cmake/mqtt_psram_buffers.cmake、components/wifi_manager/src/wifi_manager.c能力分配示例。国内访问慢可以用 Gitee 镜像gitee.com/tange-ai/xiaotai-esp32。代码开源、可自由取用。8 MB PSRAM 用不起来先查这几点这份工程开了CONFIG_SPIRAM_USE_MALLOC要用 PSRAM分配时显式指定MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT。八线模式和 80 MHz 要和硬件对上换板子先核这两项。各项开关的完整语义查 Kconfig 帮助文本。保留 128 KiB 内部 RAM 给只认内部内存的地方TRY_ALLOCATE_WIFI_LWIP这份工程没有启用。这家工程把摄像头 DMA 暂存、驱动栈和控制结构留在内部。注释记录过一次GC2145 直接用 PSRAM 做 DMA 曾损坏原始帧。第三方库的大缓冲先看有没有配置项或宏可用要改源码副本就加哈希校验。查的时候分别打印内部和 PSRAM 的堆信息。堆剩余反映不了外部 BSS 和 XIP 的占用。
企业数字化 ERP 产品动态
相关推荐
DSH插件(dsh-bridge-browser) 介绍:dsh-bridge-browser(完整包名 shallbay/dsh-bridge-browser)的核心定位很明确:让 DSH 直接操作你当前正在使用的 Chrome 标签页,而不是另开一个全新的浏览器窗口。它最大的价值是保留你已有的登录态、Cookie 和会… · 2026/11/6 1:47:27
6AQ8/ECC85 双三极管引脚识别与电路应用指南 摘要:本文面向电子管电路初学者,以 6AQ8(ECC85)双三极管为例,从管脚识别与物理朝向确认讲起,逐步覆盖灯丝供电参数设定、共阴极放大电路搭建、双单元通道隔离、中心屏蔽柱接地、静态工作点测试、常见安装错误排查、等效型号替换与极限参数监控等十个环节。全文以可落地的… · 2026/11/6 1:47:27
Learn Pi-Agent (1) 前言
用了不少 Agent,无论是 Claude Code 还是 Codex,用起来确实方便,但内置的各种 Skill、子 Agent 设置以及 MCP 协议使得代码不具可读性。想研究一下成熟的 Agent 是如何实现的,于是找了 Pi-Agent 作为学习对象。首先它开源&a… · 2026/11/6 1:47:27
pprof 使用指南(Go 1.24) 一、pprof 是什么:先建立正确的心理模型
pprof 不是一个工具,而是一条流水线:
┌─────────── 采集端(在你的进程里)───────────┐ ┌─── 分析端(在你的终端里)───┐
│ │ │ … · 2026/11/6 1:50:30
RTX 显卡算力解锁实战:gpu-unlock 让 AI 算力免费翻倍_RTX算力解锁 同样的 RTX 4090,硅片一模一样,跑 AI 时张量算力却被砍掉一半——这不是段子,而是 NVIDIA 用固件写死的"市场切割"。本文详解 GitHub 开源项目 gpu-unlock:不刷 BIOS、不改驱动、不超频,在开机阶段自动解除 … · 2026/11/6 1:50:30
memory do_set_pmd do_set_pmd 是文件映射缺页处理路径中的一个巨页建立尝试函数。当从文件系统读入的页面是一个合适的复合页(如 THP)时,do_set_pmd 会尝试跳过 PTE 层级,直接在 PMD 层级建立巨页映射。核心职责:尝试建立 PMD 巨页在 fi… · 2026/11/6 1:50:30
基于网启PXE服务器的批量定制系统平台 基于网启PXE服务器的批量定制系统平台
项目说明
概述
该项目共分为2个子项目,由iventoy和定制安装两部分组成该项目旨在复习巩固系统服务部署使用、shell编程等知识,旨在让学生增加知识面,提高项目实习经历,充实简历
项目背景知识… · 2026/11/6 1:50:30
计算机基础终极科普:一文讲透寄存器、缓存、内存、闪存的区别 1. 一句话破案:存储金字塔的“四大金刚”计算机存储世界有一个铁律:越靠近大脑(CPU),速度越快,容量越小,价格越贵;越远离大脑,速度越慢,容量越大,… · 2026/11/6 1:50:30
Zed官宣:支持Python Pyrefly,速度起飞 Zed 作为用 Rust 写的原生编辑器,一直以来的卖点就是“快得让人头皮发麻”。现在,他们觉得光编辑器快还不够,连写 Python 的那个“语言服务器(LSP)”也得换成超跑级别的。
Zed 已经把 Pyrefly 这个由 Meta 打造的、用 … · 2026/11/6 1:49:29
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07
三菱PLC数据采集到数据库的时序设计:Python+MC协议实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11
ASIC主模式RTL设计:状态机与三态驱动的流片级实现 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11
STM32嵌入式C++开发:GDB调试与Renode仿真实战指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07