CH585_IOTC_内存占用分析总结
固件的Flash和RAM的分析方法
固件Flash与静态RAM占用分析方法总结(沁恒 .map 分析法)
适用场景:沁恒 CH585(MounRiver 工程,LiteOS-M + IOTC + BLE)固件的 FLASH / 静态 RAM 占用分析。
一、分析思路总览
沁恒 MounRiver 工程编译链接
│ (链接器输出所有输入段的最终地址、大小、归属)
▼
.map 文件(obj\CH585_LiteOS_m.map)
│ (analyze_map.py 自动解析、归类、几何法计算)
▼
FLASH 总占用 + 静态 RAM 占用 + 按模块分类明细 + 校验
.map 作为数据源:它是链接器的最终产出,真实反映了每个目标文件(.o)/库(.a)的每个输入段被放到了哪个地址、占了多少字节,是 ROM/RAM 占用的权威依据,不受打印格式或估算误差影响。
二、操作步骤
第 1 步:编译生成 .map
用沁恒 MounRiver 打开工程编译,链接后在 obj\ 目录生成 .map 文件(本例为 CH585_LiteOS_m.map)。
第 2 步:用 analyze_map.py 解析
将analyze_map.py、CH585_LiteOS_m.map这两个文件放在同一目录下,并在当前目录打开控制台执行如下指令
python analyze_map.py CH585_LiteOS_m.map
- 参数为 .map 文件路径;不传参则自动检测当前目录最新的 .map
- 结果打印到控制台,同时在当前工作目录生成带时间戳的报告 mem_report_YYYYMMDD_HHMMSS.txt
工具:
analyze_map.py
三、输出报告结构
脚本输出一份 mem_report_*.txt,共五部分:
| 部分 | 内容 | 口径 |
|---|---|---|
| 一 | 输出段(Section)总览 | 每个顶层段的大小、是否占 F/R、含多少 fill |
| 二 | RAM 按模块分类 + RAM 物理布局 | 分类表是信息分解;物理布局是权威值 |
| 三 | FLASH 按模块分类 | 模块累加 + fill = 合计,给出剩余量 |
| 四 | 关键库明细 | libCH58xBLE / libiotc / libmbedtls / LiteOS 的分段构成(F/R/B 标记) |
| 五 | 校验 | 段头合计 vs 模块累加的差额 |
固件的动态RAM的分析方法总结
一、原理
LiteOS-M 的动态内存是一个静态定义的堆数组:
// Kernel\kernel_liteos_m\kernel\src\mm\los_memory.c
STATIC UINT8 g_memStart[LOSCFG_SYS_HEAP_SIZE];
- 该数组落在 .bss 段,大小由 target_config.h 中的 LOSCFG_SYS_HEAP_SIZE 决定;
- 所有任务栈、队列、信号量、定时器、以及组件运行时的 malloc 类分配,都从这个堆里出;
- 堆调小 → .bss 变小 → 静态 RAM 占用下降;但若小于真实需求,任务创建/内存分配失败,系统跑不起来。
LOSCFG_SYS_HEAP_SIZE 存在一个临界值:
堆 > 临界值 → 系统正常运行
堆 < 临界值 → 分配失败,系统异常/跑飞
临界值 ≈ 当前固件实际需要的动态内存总量
因此,逐步调小堆,用"系统是否还能正常运行"作为探针,找到的最小可用值就是动态内存需求。
二、如何把"组件"的消耗单独剥离
IOTC 组件的入口在 main.c 中被显式调用:
// Src\APP\main.c (main/任务入口内)
extern void IotcOhDemoEntry(void);
IotcOhDemoEntry();
通过注释 / 放开这两行做对照实验,即可把消耗拆分为:
状态A(注释掉 IotcOhDemoEntry):系统堆 = 内核(+BLE) 基线消耗
状态B(放开 IotcOhDemoEntry) :系统堆 = 内核(+BLE) + IOTC 组件消耗
→ IOTC 组件动态内存 = 临界值B − 临界值A
三、操作步骤
第 1 步:测内核(+BLE)基线临界值
- 注释 Src\APP\main.c 中两行,不启动 IOTC 组件:
// extern void IotcOhDemoEntry(void);
// IotcOhDemoEntry();
- 打开 Kernel\kernel_liteos_m\target_config.h,修改堆大小:
//#define LOSCFG_SYS_HEAP_SIZE 0x02800UL
#define LOSCFG_SYS_HEAP_SIZE (XXXXX) // 逐步调小
- 每改一次 → 重新编译烧录 → 观察系统是否正常运行;
- 用二分法逼近:能正常跑的最小值,即临界值A(内核基线)。
第 2 步:测 内核(+BLE)+IOTC 临界值
- 恢复 main.c 中两行(放开 IotcOhDemoEntry() 调用);
- 同样逐步调小 LOSCFG_SYS_HEAP_SIZE,逼近系统还能正常完成 IOTC 连接/业务的最小值;
- 即 临界值B(内核+IOTC)。
第 3 步:计算
内核(+BLE) 动态内存 ≈ 临界值A
IOTC 组件动态内存 ≈ 临界值B − 临界值A
固件总动态内存 ≈ 临界值B
四、"临界"的判定标准
堆不足时典型现象(任一出现即说明低于临界值):
- 启动打印 LiteOS heap memory address:..., size:0x... 后系统卡死/跑飞;
- 任务创建失败(LOS_TaskCreate 返回错误);
- 运行中业务异常、 HardFault;
- IOTC 无法完成连接或中途异常断开。
建议每档值至少跑完整业务流程并稳定运行数分钟再判定"能跑",避免临界值定得偏小留下隐患。
5、辅助验证(可选)
可在工程内添加内存打印信息代码,在沁恒 Src\APP\main.c 的工程文件下加入如下代码:
#include "los_memory.h"
#include "cmsis_os2.h"
void MemInfoTaskFunc(void)
{
LOS_MEM_POOL_STATUS poolStatus = {0};
/* pool为要统计信息的内存地址,此处以OS_SYS_MEM_ADDR为例 */
void *pool = OS_SYS_MEM_ADDR;
LOS_MemInfoGet(pool, &poolStatus);
/* 算出内存池当前的碎片率百分比 */
// float fragment = 100 - poolStatus.maxFreeNodeSize * 100.0 / poolStatus.totalFreeSize;
/* 算出内存池当前的使用率百分比 */
// float usage = LOS_MemTotalUsedGet(pool) * 100.0 / LOS_MemPoolSizeGet(pool);
// printf("usage = %f, fragment = %f, maxFreeSize = %d, totalFreeSize = %d, waterLine = %d\n", usage, fragment,
// poolStatus.maxFreeNodeSize, poolStatus.totalFreeSize, poolStatus.usageWaterLine);
PRINTK("totalUsedSize = %d, maxFreeSize = %d, totalFreeSize = %d, waterLine = %d\n",
poolStatus.totalUsedSize, poolStatus.maxFreeNodeSize, poolStatus.totalFreeSize, poolStatus.usageWaterLine);
}
static void vXtsTask1(void *argument)
{
PRINTK("vXtsTask1 enter\r\n");
// osDelay(600 * osKernelGetTickFreq() );
for (;;)
{
osDelay(1000 * osKernelGetTickFreq() / 1000);
PRINTK("vXtsTask1\r\n");
MemInfoTaskFunc();
}
}
#define TEST_TASK_PRIO 29
static void memoryTask(void)
{
osThreadAttr_t attrTemp = {0};
attrTemp.name = "vXtsTask1";
attrTemp.stack_size = 0x400;
attrTemp.priority = TEST_TASK_PRIO;
// Create semaphore
PRINTK("vStartXtsTask !\r\n");
// Create ble Task
osThreadNew(vXtsTask1, NULL, &attrTemp);
}
LITE_OS_SEC_TEXT_INIT int main(void){
...
memoryTask();
...
}
在Kernel\kernel_liteos_m\arch\WCH\Qingke_V3C\gcc\riscv_bits.h中修改如下配置:
#define REGBYTES 16
添加内存打印信息后可在运行时打印堆的总量/已用/空闲/水位。可与临界值法交叉验证:
- 临界值法 → 得到"系统还能跑"的最小堆(下界);
- 运行时水位 → 看到实际峰值使用量(更精确,且能定位是哪个阶段涨的内存)。
两者互相印证,临界值应 ≥ 运行时水位。
更多推荐
所有评论(0)