一、常用方法
常用方法:
- CPU 周期计数器
- 片上定时器 / SysTick
- GPIO 翻转 + 示波器 / 逻辑分析仪
- RTOS Trace / 运行时间统计
1.1 CPU 周期计数器
CPU 周期计数器是内核自带的调试模块,每一个 CPU 周期自增 1,具有精度最高(纳秒级)、侵入性最小的优势,是函数级测量的首选方案。
工作原理
以 Cortex-M 系列内核为例,通过使能 DWT(Data Watchpoint and Trace)模块的 CYCCNT 计数器,记录代码执行前后的计数值,计算差值后除以 CPU 频率,即可得到执行时间。
例:
#include "core_cm4.h"
// 初始化周期计数器
static inline void cycle_counter_init(void) {
// 1. 使能DWT模块
if (!(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)) {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
}
DWT->CYCCNT = 0; // 2. 清零计数器
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // 3. 开启计数
}
// 获取当前周期计数值
static inline uint32_t cycle_counter_get(void) {
return DWT->CYCCNT;
}
// 测量示例
void measure_function(void) {
cycle_counter_init(); // 初始化(可全局执行一次)
uint32_t start = cycle_counter_get();
// 被测代码块
foo();
bar();
uint32_t end = cycle_counter_get();
uint32_t delta_cycles = end - start; // 无符号溢出自动处理
float delta_us = (float)delta_cycles / (SystemCoreClock / 1000000); // 换算为微秒
printf("[PROF] 执行时间:%lu 周期 / %.2f us\r\n", delta_cycles, delta_us);
}
适用场景
- 微秒级甚至纳秒级的高精度测量;
- 函数、中断服务程序等小粒度代码块;
- 无 RTOS 的裸机环境或轻量 RTOS 环境。
1.2 片上定时器 / SysTick:工程折中方案
当芯片不支持 DWT 模块,或需要与 CPU 频率解耦的时间基准时,片上通用定时器是最常用的选择。通过配置定时器为自由运行模式,以固定频率计数,适合中粒度时间测量。
工作原理
配置定时器的预分频系数和自动重装载值,使其以已知频率(如 1MHz,即 1tick=1us)递增,测量前后读取计数器值,计算差值即为时间。
例:
#include "stm32f4xx_hal.h"
static TIM_HandleTypeDef htim2;
// 初始化定时器(1MHz计数,1tick=1us)
void timer_profiling_init(void) {
__HAL_RCC_TIM2_CLK_ENABLE();
htim2.Instance = TIM2;
htim2.Init.Prescaler = 83; // APB1时钟84MHz,84MHz/(83+1)=1MHz
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0xFFFFFFFF; // 32位计数,减少溢出
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_Base_Init(&htim2);
HAL_TIM_Base_Start(&htim2); // 自由运行模式启动
}
// 获取当前计数值
static inline uint32_t timer_profiling_get_ticks(void) {
return __HAL_TIM_GET_COUNTER(&htim2);
}
// 测量示例
void measure_task(void) {
timer_profiling_init(); // 全局初始化一次
uint32_t start = timer_profiling_get_ticks();
// 被测代码块
task_run();
uint32_t end = timer_profiling_get_ticks();
// 处理计数器溢出
uint32_t delta_ticks = (end >= start) ? (end - start) : (0xFFFFFFFFu - start + 1u + end);
float delta_us = (float)delta_ticks; // 1tick=1us
printf("[PROF] 任务执行时间:%.2f us\r\n", delta_us);
}
适用场景
- 几十微秒到秒级的中粒度代码块;
- 需与 CPU 频率解耦的测量场景;
- 不支持 DWT 模块的芯片。
1.3 GPIO 翻转 + 示波器:最直观的可视化方案
这种方法原理简单但实用性极强,通过 GPIO 电平翻转标记测量起止,用示波器或逻辑分析仪测量脉宽,可避免软件测量的侵入性影响,误差极小。
工作原理
- 测量开始前:将指定 GPIO 引脚拉高;
- 测量结束后:将该 GPIO 引脚拉低;
- 示波器测量高电平脉宽,即为代码执行时间。
例:
#include "stm32f4xx_hal.h"
// 定义测量GPIO(需提前初始化为输出模式)
#define PROF_GPIO_Port GPIOA
#define PROF_Pin GPIO_PIN_0
// 测量宏定义
#define MEASURE_BEGIN() HAL_GPIO_WritePin(PROF_GPIO_Port, PROF_Pin, GPIO_PIN_SET)
#define MEASURE_END() HAL_GPIO_WritePin(PROF_GPIO_Port, PROF_Pin, GPIO_PIN_RESET)
// 测量示例
void process_data_measure(void) {
MEASURE_BEGIN();
// 被测代码块
process_sensor_data();
update_control_output();
MEASURE_END();
}
适用场景
- 调试阶段的直观验证;
- 需排除软件测量干扰的场景;
- 多信号时序对齐测量(如同步观察多个代码块执行时间)。
1.4 RTOS 运行时间统计:系统级瓶颈分析
在 RTOS 环境中,可通过系统自带的运行时间统计功能,获取各任务的 CPU 占用率,适合系统级性能优化和瓶颈定位。
工作原理(以 FreeRTOS 为例)
- 启用统计功能后,FreeRTOS 通过高精度定时器维护每个任务的运行时间计数器;
- 任务切换时,内核计算当前任务的运行时长并累加;
- 通过 API 函数可获取各任务的运行时间占比,生成统计报表。
例:
#define configGENERATE_RUN_TIME_STATS 1 // 启用运行时间统计
#define configUSE_STATS_FORMATTING_FUNCTIONS 1 // 启用格式化输出函数
extern void vConfigureTimerForRunTimeStats(void); // 定时器初始化钩子
extern unsigned long ulGetRunTimeCounterValue(void); // 计数值获取钩子
-
实现钩子函数与定时器初始化:
// 定时器初始化(复用片上定时器方案)
void vConfigureTimerForRunTimeStats(void) {
timer_profiling_init(); // 1MHz计数,1tick=1us
}// 获取当前计数值
unsigned long ulGetRunTimeCounterValue(void) {
return (unsigned long)timer_profiling_get_ticks();
} -
创建统计打印任务:
#define RUNTIME_STATS_BUF_LEN 256
void vTaskPrintStats(void *pvParameters) {
char buf[RUNTIME_STATS_BUF_LEN];
for (;;) {
vTaskDelay(pdMS_TO_TICKS(1000)); // 每秒打印一次
memset(buf, 0, RUNTIME_STATS_BUF_LEN);
vTaskGetRunTimeStats(buf); // 生成统计报表
printf("===== 任务运行时间统计 =====\r\n%s\r\n", buf);
}
} -
典型输出结果:
===== 任务运行时间统计 =====
Task Run Time PercentagectrlTask 350000 35%
commTask 250000 25%
logTask 150000 15%
idle 250000 25%
适用场景
- 多任务 RTOS 系统的全局性能分析;
- 定位 CPU 占用率过高的任务;
- 系统架构优化与负载均衡调整。
二、方法选型指南
| 测量需求 | 推荐方法 | 优势 | 局限性 |
|---|---|---|---|
| 函数 / 中断级、微秒级精度 | CPU 周期计数器(DWT) | 精度最高、侵入性最小 | 依赖内核支持、需 CPU 频率稳定 |
| 任务级、10μs~ 秒级 | 片上定时器 / SysTick | 实现简单、与 CPU 频率解耦 | 精度中等、需处理计数器溢出 |
| 调试阶段、直观验证 | GPIO 翻转 + 示波器 | 无软件干扰、可视化强 | 需硬件工具、不适用于生产环境 |
| 系统级、任务占比分析 | RTOS 运行时间统计 | 全局视角、定位系统瓶颈 | 仅支持 RTOS、无法测量单个函数 |