1、TWS内存全景:了解TWS耳机系统架构,掌握SRAM、Flash、PSRAM等内存类型及其在TWS中的角色

做TWS嵌入式开发的朋友,咱们今天来聊聊内存。

说实话,我刚入行那会儿,也被TWS里这些SRAM、Flash、PSRAM搞得有点晕。明明都是存储,为什么分这么多种?后来踩了几个坑才明白——每种内存都有自己的脾气,用对了地方,事半功倍;用错了,轻则跑不起来,重则量产翻车。

1.1 TWS耳机系统架构速览

先看一张简化的架构图,我习惯把TWS耳机拆成三个核心域:

  • 主控芯片(SoC):通常是ARM Cortex-M系列或RISC-V内核,负责蓝牙协议栈、音频编解码、降噪算法等。
  • 存储子系统:包括内部SRAM、外部PSRAM、以及存放固件的Flash。
  • 外设接口:I2S(音频)、I2C/SPI(传感器)、UART(调试)等。

你想想看,耳机那么小,电池就那么点,主频还不能太高。所以内存的选型和使用,直接决定了你的固件能不能塞进去、跑得顺不顺、功耗压不压得住。

核心观点:TWS的内存优化,本质是在容量、速度、功耗、成本之间找平衡。没有银弹,只有取舍。

1.2 SRAM:你的“工作台”

SRAM,静态随机存取存储器。说白了就是CPU的“桌面”——所有正在运行的代码、变量、堆栈,都得放在这里。

我在项目中遇到过一个问题:某款芯片内部SRAM只有512KB,但蓝牙协议栈+音频算法就占了400KB,留给应用层的只剩100KB出头。结果一跑OTA升级,内存直接爆了。

SRAM的特点

  • 速度快,跟CPU同频,零等待周期
  • 功耗相对低,但容量做不大(成本高)
  • 掉电数据丢失

嗯,这里要注意:SRAM虽然快,但千万别把所有数据都往里塞。我建议把实时性要求高的数据(比如音频缓冲区、中断上下文)放SRAM,其他能放PSRAM的就放PSRAM。

避坑指南:我曾经在SRAM里放了一个很大的查找表,结果导致音频中断响应延迟。后来把查找表移到PSRAM,用DMA预取,问题就解决了。记住——SRAM不是仓库,是工作台。

1.3 Flash:你的“硬盘”

Flash,非易失性存储器。固件、配置文件、蓝牙配对信息、语音提示音,都存这里。

TWS里用的Flash通常是NOR Flash,容量从1MB到16MB不等。为什么不用NAND?因为NOR支持XIP(就地执行),代码可以直接在Flash上跑,不用先拷到SRAM里。

Flash的角色

  • 存放固件镜像(代码段+只读数据)
  • 存放用户配置(EQ参数、ANC校准值)
  • 存放OTA升级的备份区

我个人习惯把Flash分区规划好:Bootloader区、主固件区、备份区、配置区。每个区的大小要提前算死,不然后期改分区表,牵一发动全身。

注意:Flash有擦写寿命(通常10万次),别把频繁变化的数据(比如计步器的步数)直接写Flash。我见过有人每秒写一次Flash,结果三个月后耳机变砖了。

1.4 PSRAM:你的“扩展内存”

PSRAM,伪静态随机存取存储器。它本质上是DRAM,但内部集成了刷新逻辑,对外接口跟SRAM一样。

为什么要用PSRAM?因为芯片内部的SRAM太贵了,做不大。而PSRAM可以用更低的成本提供更大的容量(比如8MB、16MB)。

PSRAM的典型用途

  • 音频解码缓冲区(比如AAC、LDAC解码需要大buffer)
  • 图形界面帧缓冲(如果有屏的话)
  • 大数据处理(比如语音识别的前端处理)

但PSRAM有个坑——访问速度比SRAM慢,而且有延迟。你想想看,如果CPU频繁访问PSRAM,性能会打折扣。所以我的做法是:用DMA来搬运数据,让CPU专心算,别等内存。

经验之谈:我做过一个项目,PSRAM的初始化时序没调好,导致系统偶尔死机。后来用逻辑分析仪抓波形,发现是PSRAM的片选信号毛刺。加了个小延时就好了。嗯,硬件上的小问题,软件上也能补。

1.5 三种内存的对比

类型 速度 容量 成本 掉电保持 典型用途
SRAM 最快(零等待) 小(几十KB~几MB) 堆栈、中断向量、音频buffer
Flash 较慢(读快写慢) 中(1MB~16MB) 固件、配置、语音提示
PSRAM 中等(有延迟) 大(4MB~64MB) 解码buffer、图形帧缓冲

1.6 内存分配的基本原则

做了这么多年嵌入式,我总结了几条内存分配的铁律:

  1. 热数据放SRAM,冷数据放Flash:频繁访问的变量、中断服务函数,必须放SRAM。不常用的配置参数,放Flash。
  2. 大块数据放PSRAM:比如音频解码的中间缓冲区,用PSRAM比用SRAM划算得多。
  3. 静态分配优先:TWS这种资源紧张的系统,尽量别用malloc。我见过有人动态分配导致内存碎片,最后系统崩溃。
  4. 对齐访问:ARM Cortex-M要求4字节对齐,不对齐会触发异常。嗯,这个坑我踩过。

一个小技巧:在链接脚本里,把不同属性的数据放到不同的内存区域。比如:.sram_data 放SRAM,.psram_data 放PSRAM。这样代码里一目了然,也方便后期优化。

1.7 一个实际的内存布局示例

假设我们用的芯片内部有512KB SRAM,外挂8MB PSRAM和4MB Flash。我通常会这样规划:

/* 链接脚本示例(简化版) */
MEMORY
{
    SRAM    (rwx) : ORIGIN = 0x20000000, LENGTH = 512K
    PSRAM   (rw)  : ORIGIN = 0x60000000, LENGTH = 8M
    FLASH   (rx)  : ORIGIN = 0x08000000, LENGTH = 4M
}

SECTIONS
{
    /* 中断向量表放Flash */
    .isr_vector : { *(.isr_vector) } > FLASH
    
    /* 代码段放Flash,支持XIP */
    .text : { *(.text*) } > FLASH
    
    /* 快速运行的代码拷到SRAM */
    .fast_code : { *(.fast_code*) } > SRAM AT > FLASH
    
    /* 全局变量放SRAM */
    .data : { *(.data*) } > SRAM AT > FLASH
    
    /* 大缓冲区放PSRAM */
    .psram_buffer : { *(.psram_buffer*) } > PSRAM
}

你看,这样分下来,每个区域干什么清清楚楚。我建议你在项目初期就把内存布局画出来,别等到代码写了一半才想起来改。

1.8 本章小结

好了,咱们把TWS的内存全景捋了一遍:

  • SRAM是工作台,快但贵,放实时数据
  • Flash是硬盘,掉电不丢,放固件和配置
  • PSRAM是扩展内存,便宜容量大,放大块数据

下一章,我会带你深入SRAM,看看怎么把每一字节都榨干。说实话,内存优化这事儿,越做越有意思。你想想看,在那么小的芯片里,把每一比特都安排得明明白白,是不是很有成就感?

一句话记住:TWS内存优化的第一步,不是优化,而是搞清楚你的内存到底是怎么用的。