在蓝牙耳机软件开发中,RAM(Random Access Memory)存储器起着至关重要的作用。主要体现在以下方面:
- 存储程序运行时的数据。主要是系统中的一些变量、通信数据临时存储缓冲区等等。
- 存储部分程序代码。在一些嵌入式系统中,为了加快程序的启动速度,部分代码可能会被加载到 RAM 中执行。
- 提供系统运行时临时存储空间。比如函数调用和返回的地址存储,提供中断上下文存储区。(通常是栈空间)
- 实现数据共享和通信。比如不同模块之间的通信传递的消息和数据,多任务环境中的数据共享等。
杰理SDK中的RAM
以下我们以杰理JL710N芯片为例子,从SDK介绍来看总共可用RAM大小为239KB
下图为SDK中sdk_ld.c文件注释说明的RAM资源的分布情况
sdk_ld.c文件是一个用于生成链接脚本的C语言源代码文件,该文件通过预处理展开宏后,生成最终用于链接的.ld文件,即链接脚本。链接脚本用于控制程序在内存中的布局,包括代码段、数据段、只读数据段等的起始地址、大小以及它们在内存中的分布情况。
以上可以看到MEMORY位置只有一个ram0,LENGTH即为芯片配置的RAM_SIZE。
编译时分配的RAM
编译时分配的RAM是指在编译阶段,由编译器为程序中的全局变量、静态变量等分配的内存空间。比如使用静态全局变量或静态局部变量时,编译器会根据变量的类型和初始化声明,在编译过程中确定所需的内存大小,并将这些变量分别存储在特定的内存段中,(.data 段或 .bss 段)这些段的大小和位置在编译时是固定的。
以下还是以JL710N为例,SDK编译完成后,芯片RAM空间实际的分布情况。
如下图,由map文件memory configuration 也可以查看到编译完成后最终RAM的分布情况,编译出来的map文件生成在SDK/cpu/brxx/tools目录下。这里我们可以看到ram0起始位置0x100000,length为0x3b800,即 总共大小为238KB,所以在map文件主要看地址0x100000~0x13b800的位置就是这个SDK中RAM的存放位置。
由于编译时.data、.bss和部分.code就占用了一部分内存,堆内存从地址0x113ab0开始;也就是供代码运行时能分配的内存有0x13b800-0x113ab0=0x27d50(也就是163152字节)
运行时分配的RAM
运行时分配的RAM是在程序运行过程中,通过调用内存管理函数(如动态内存分配和释放函数)或者通过自动内存管理机制(如栈的动态增长和收缩)来分配和回收内存。
系统开机运行时可以开启内存信息的实时打印,以获取到内存使用的最新状态。
在audio_debug中会以我们设置的时间间隔输出实时内存信息。由下图可以看到,总heap大小为上面我们计算出来的163152字节,剩余可供代码申请使用physics memory大小为102656字节。
杰理SDK内存不足情况
在使用杰理蓝牙芯片蓝牙耳机软件开发过程中,用户可能经常出现No enough physics memory的死机异常问题。出现这种情况通常表示系统的物理内存不足,无法满足当前程序或操作的需求。受到芯片硬件RAM资源的限制,在软件上开启了过多功能、代码中RAM资源分配不合理、内存泄漏或系统配置等问题都有可能会导致系统运行中出现这种内存不足的情况。下图为出现物理内存不足时系统打印内存不足死机异常的情况,通常也称之为爆RAM的情况。
杰理SDK内存优化方向
调整任务栈空间的大小
调整任务栈空间的大小以节省RAM内存是嵌入式系统开发中的常见优化手段。任务栈用于存储函数调用的返回地址、任务中的局部变量、中断/上下文切换时的寄存器状态、函数参数等,栈空间过大会浪费RAM,过小会导致栈溢出。这里我们在杰理SDK中的app_main.c文件中任务列表可以适当缩小,以调整任务栈的空间。
如何进行适当的调整也是一个调整的关键,这里我们可以借助可视化工具中的“设备状态工具”来查看系统运行时各个任务栈的栈大小。根据历史使用最大栈做优化和调整,适当缩小任务栈的大小,避免RAM资源浪费。
将部分代码从RAM中移出
SDK默认将部分代码放入RAM中是考虑到CPU访问RAM速度比访问FLASH快得多,一些频繁调用的核心函数或关键算法代码放在RAM中,可以减少任务切换和其他操作的延迟,确保系统快速响应。其次是RAM的访问过程相对ROM的访问较为节能。对于蓝牙耳机来说核心部分的代码可放在RAM中,可以减少由于频繁访问FLASH存储器带来的能量损耗从而节省功耗。
如下图在杰理SDK中默认将一些算法放在了RAM中,在资源紧张的情况我们可以把这些算法从RAM中移出,放到FLASH中。在audio_config_def.h中将部分算法从RAM中移出,以达到节省RAM空间的目的。
部分缓冲区大小的裁剪
音频数据从手机发送到蓝牙耳机时,这些数据会先存储在缓冲区内,蓝⽛耳机的软件通常会设计⼀个缓存区来暂存接收到的⾳频数据,然后从这⾥取出数据进⾏处理并最终传递到⾳频播放模块。设置缓冲区的目的往往是为了⾳频品质和系统稳定性。在RAM资源非常紧张的情况可以考虑减小缓存区的大小,并根据实际测试结果,对A2DP缓存的大小进行迭代调整。如下图调整A2DP缓存区的大小。