当前位置: 首页 > news >正文

LVGL和FreeRTOS可视化跟踪

一、LVGL用量跟踪

lv_mem_monitor_t mon; lv_mem_monitor(&mon); printf("\r\n========== LVGL Memory Info ==========\r\n"); printf("Total size: %d bytes (%.1f KB)\r\n", mon.total_size, mon.total_size / 1024.0); printf("Free size: %d bytes (%.1f KB)\r\n", mon.free_size, mon.free_size / 1024.0); printf("Used size: %d bytes (%.1f KB)\r\n", mon.total_size - mon.free_size, (mon.total_size - mon.free_size) / 1024.0); printf("Max used (peak): %d bytes (%.1f KB)\r\n", mon.max_used, mon.max_used / 1024.0); printf("Used percent: %d%%\r\n", mon.used_pct); printf("Free blocks: %d\r\n", mon.free_cnt); printf("Used blocks: %d\r\n", mon.used_cnt); printf("Biggest free block:%d bytes\r\n", mon.free_biggest_size); printf("Fragmentation: %d%%\r\n", mon.frag_pct); printf("=====================================\r\n");
========== LVGL Memory Info ========== Total size: 20480 bytes (20.0 KB) Free size: 9764 bytes (9.5 KB) Used size: 10716 bytes (10.5 KB) Max used (peak): 11940 bytes (11.7 KB) Used percent: 53% Free blocks: 7 Used blocks: 273 Biggest free block:9484 bytes Fragmentation: 3% =====================================

一、字段含义详解

字段类型含义说明
mon.total_sizeuint32_t总内存大小LVGL 动态内存池的总大小(即LV_MEM_SIZE配置的值)
mon.free_sizeuint32_t当前空闲内存当前未使用的内存字节数
mon.free_biggest_sizeuint32_t最大空闲块大小内存池中最大的连续空闲块(碎片化程度指标)
mon.free_cntuint32_t空闲块数量内存池中被分割成多少块空闲区域(越多说明碎片越严重)
mon.used_cntuint32_t已使用块数量当前已分配的内存块数量
mon.max_useduint32_t历史最大使用量从启动到现在的峰值使用量(字节)
mon.used_pctuint8_t使用百分比当前使用量占总量的百分比(0-100%)
mon.frag_pctuint8_t碎片化百分比内存碎片化程度(0-100%,越高越严重)

根据这些信息判断内存是否够用

指标健康状态需要关注危险
free_size> 20% 总量10-20%< 10%
frag_pct< 30%30-50%> 50%
free_biggest_size> 预期单次分配大小-< 预期单次分配大小

LVGL 内存分析

当前配置

项目状态
LV_MEM_SIZE20,480 字节 (20 KB)
当前使用10,716 字节 (10.5 KB)
峰值使用11,940 字节 (11.7 KB)
空闲内存9,764 字节 (9.5 KB)✅ 充裕
碎片化3%✅ 非常健康

二、FreeFTOS任务跟踪

FREERTOS_CONFIG_H //任务跟踪 extern volatile uint32_t CPU_RunTime; #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() (CPU_RunTime = 0ul) #define portGET_RUN_TIME_COUNTER_VALUE() CPU_RunTime /* Run time and task stats gathering related definitions. */ #define configGENERATE_RUN_TIME_STATS 1//启用运行时间统计功能 #define configUSE_TRACE_FACILITY 1//启用可视化跟踪调试 #define configUSE_STATS_FORMATTING_FUNCTIONS 1//开启后才能使用vTaskList
--------------------------------------------- 任务名 任务状态 优先级 剩余栈 任务序号 Tmr Svc X 31 172 5 IDLE R 0 118 4 led2 B 2 42 2 led1 B 1 106 1 Lvgl B 5 650 3 ---------------------------------------------

一、整体概念

FreeRTOS 的任务运行时间统计功能,核心思想是:

用一个高精度计数器来记录时间,每个任务开始运行时记录开始值,任务切换时计算差值,累加到该任务的运行时间中。


二、关键宏定义的含义

c

extern volatile uint32_t CPU_RunTime; // 全局时间计数器 // 1. 初始化函数:重置计数器 #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() (CPU_RunTime = 0ul) // 2. 获取当前时间:返回当前计数值 #define portGET_RUN_TIME_COUNTER_VALUE() CPU_RunTime

这两个宏的作用

调用时机作用
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()系统启动时(vTaskStartScheduler()内)调用一次初始化时间计数器
portGET_RUN_TIME_COUNTER_VALUE()每次任务切换时调用获取当前时间值,用于计算任务运行时长

三、你的实现流程

1. 定时器5提供时间基准(20kHz)

c

// TIM5 中断服务函数(20kHz = 50us 一次) void TIM5_IRQHandler(void) { if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM5, TIM_IT_Update); CPU_RunTime++; // 每 50us 增加 1 } }

时间单位:

  • 20kHz 中断频率 → 每次中断间隔 = 1 / 20000 = 50 微秒

  • CPU_RunTime每增加 1,代表过去了 50 微秒


2. 系统启动时初始化

c

// FreeRTOS 内核启动时(vTaskStartScheduler() 内部) // 会调用 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() // 相当于执行: CPU_RunTime = 0; // 从 0 开始计数

3. 任务切换时计算运行时间

FreeRTOS 内核在任务切换时会执行以下逻辑(简化版):

c

// 当前任务要切出了,计算它运行了多久 void vTaskSwitchContext(void) { // 1. 获取当前时间 uint32_t current_time = portGET_RUN_TIME_COUNTER_VALUE(); // = CPU_RunTime // 2. 计算当前任务的运行时间增量 uint32_t delta = current_time - last_time; // last_time 是上次切换时的时间 // 3. 累加到当前任务的运行时间统计中 pxCurrentTCB->ulRunTimeCounter += delta; // 4. 记录当前时间,供下次计算用 last_time = current_time; // 5. 切换到下一个任务... }

四、数据流向图

text

TIM5 中断 (20kHz) ↓ CPU_RunTime++ (每 50us +1) ↓ [时间计数器] ↓ ┌────────────────┼────────────────┐ ↓ ↓ ↓ 任务切换时 任务切换时 任务切换时 ↓ ↓ ↓ 计算 Task1 计算 Task2 计算 Task3 运行时间 运行时间 运行时间 ↓ ↓ ↓ Task1.counter Task2.counter Task3.counter 增加 delta 增加 delta 增加 delta

五、最终输出:vTaskGetRunTimeStats()

当你调用vTaskGetRunTimeStats(info_buffer)时,会输出类似:

text

Task Name Abs Time % Time -------------- ---------- ------- Idle Task 12345678 45% Task_LED 8765432 32% Task_ADC 6543210 23%

计算公式

text

某任务的运行时间 = 该任务的 ulRunTimeCounter × 时间单位 总运行时间 = 所有任务 ulRunTimeCounter 之和 CPU 使用率 = (某任务运行时间 / 总运行时间) × 100%

六、配置含义

方案1:使用vTaskList()查看任务状态(不需要运行时间统计)

如果你只需要查看任务状态(任务名、状态、优先级、堆栈剩余),不需要 CPU 运行时间统计:

c

/* Run time and task stats gathering related definitions. */ #define configGENERATE_RUN_TIME_STATS 0 // 改为 0 #define configUSE_TRACE_FACILITY 1 #define configUSE_STATS_FORMATTING_FUNCTIONS 1 // 改为 1(vTaskList 需要)

此时可以使用:

  • vTaskList()✅ 可用


方案2:使用vTaskGetRunTimeStats()查看 CPU 使用率(需要运行时间统计)

如果你需要查看每个任务的 CPU 使用率:

c

/* Run time and task stats gathering related definitions. */ #define configGENERATE_RUN_TIME_STATS 1 #define configUSE_TRACE_FACILITY 1 #define configUSE_STATS_FORMATTING_FUNCTIONS 1 // 改为 1

此时可以使用:

  • vTaskList()✅ 可用

  • vTaskGetRunTimeStats()✅ 可用


方案3:只统计运行时间,不输出格式化信息(不常用)

c

#define configGENERATE_RUN_TIME_STATS 1 #define configUSE_TRACE_FACILITY 1 #define configUSE_STATS_FORMATTING_FUNCTIONS 0

这种情况下:

  • 运行时间统计功能已启用,但vTaskGetRunTimeStats()函数不存在

  • 需要自己实现统计信息的输出

  • 一般不推荐这样用

七、软件定时器时机

uint8_t CPU_RunInfo[400] = {0}; void vTimerCallback( TimerHandle_t xTimer ) { //回调函数内部尽量不要使用延时 阻塞性质的操作 memset(CPU_RunInfo,0,400); //信息缓冲区清零 vTaskList((char *)&CPU_RunInfo); //获取任务运行时间信息 printf("---------------------------------------------\r\n"); printf("任务名 任务状态 优先级 剩余栈 任务序号\r\n"); printf("%s", CPU_RunInfo); printf("---------------------------------------------\r\n"); } //任务跟踪 timer1 = xTimerCreate("Timer1",1000,pdTRUE,(void*)0,vTimerCallback); // 启动定时器 if(timer1 != NULL) { xTimerStart(timer1, 1000); // 最多等待 1 秒 }

一、核心概念

定时器周期 = 采样间隔

采样间隔决定了你能看到系统行为的精细程度响应速度


二、两个关键影响

1. 时间分辨率
  • 周期越短→ 采样越密集 → 能看到更细微的状态变化

  • 周期越长→ 采样越稀疏 → 可能错过中间状态

2. 响应延迟
  • 周期越短→ 问题被发现得越快

  • 周期越长→ 问题暴露得越晚


三、取舍关系

方向优点代价
周期短细节丰富、响应快CPU占用高、输出量大
周期长资源占用低信息粗糙、响应慢
http://www.cnnetsun.cn/news/1693137.html

相关文章:

  • dji 妙算3编译ffmpeg启用h264_nvmpi h264_nvenc硬件加速
  • ProperTree完全指南:3个步骤掌握跨平台plist文件编辑技巧
  • 财务表格模板合集|会计做账、报表分析一站式搞定
  • 首粉双拼,ia没有ua在一起,有点不规范,其余首右双拼相同
  • C++的constexpr虚函数与编译期多态在模板元编程中的探索
  • WEEX 宣布赞助职业赛车手 Carl Moon,开启 2026 赛季全球品牌合作
  • 吉他弹唱资源合集(第二辑)
  • DREAM3D完整入门指南:5步掌握材料科学数据分析开源工具
  • 基于51单片机的温控风扇(温度、PID、Proteus、原理图等)资料汇编
  • BomGw v1.0软网关后台服务程序安装说明书
  • 十一,MySQL日志篇之undo-log、redo-log、bin-log
  • 万象视界灵坛应用落地:内容审核中‘深夜办公室’等场景零样本识别实操
  • Jupyter notebook打不开本地文件,有关目录存放问题
  • 如何用Sunshine搭建终极游戏串流服务器:免费跨平台完整指南
  • Fluwx终极指南:5步实现Flutter微信集成完整解决方案
  • B站成分检测器:3分钟告别评论区信息过载,智能识别用户背景
  • KART-RERANK在AIGC内容审核中的应用:自动化识别与排序低质生成文本
  • GIF动图制作神器,视频提取转GIF
  • LeetCode--454.四数相加 II(哈希表)
  • Typora标题自动编号完全指南-零代码基础实现自动化文档结构
  • 基于五一单片机的无线控制条幅悬挂机
  • 新手入门:借助快马平台生成qclaw官网代码,学习前端项目结构
  • 2.Linux.0403.随堂笔记
  • QMC解密工具:释放你的音乐自由,告别格式枷锁
  • 2026中国背景调查服务商综合实力调研:第一梯队格局与标杆研判
  • 2025届最火的十大降重复率助手解析与推荐
  • 2026年理工科大量公式数据的论文怎么降AI:特殊内容处理技巧
  • 新手友好:在快马平台用AI生成你的第一个ccswitch模拟学习项目
  • MangoHud快捷键迁移技巧:从其他软件高效迁移设置的完整指南
  • 【Ease UI】2026-04-03组件更新:新增组件xly-file-preview文件预览组件