RTOS-F429-HAL-任务运行时间查询API(2026/7/31)
2026/7/31 11:54:12 网站建设 项目流程

目录

一:vTaskGetRunTimeStats() — 任务运行时间统计

1:函数原型

2:需要的三个宏(缺一不可)

3:输出格式

4:跟 uxTaskGetSystemState() 的关系

5:时基定时器的要求

6:跟昨天学的 API 对比:

二:最终改动清单 — rtos\8\ 任务运行时间统计实验

TIM6.h

TIM6.c

stm32f4xx_it.c

FreeRTOSConfig.h

main.c

freertos_demo.c — 整个文件重写

Key.c

完整中断链路

跟正点代码的对比

三:rtos\8 任务运行时间轴

启动阶段

start_task(优先级 1,活不过 1ms)

稳态运行(无限循环)

CPU 占用占比(为什么 IDLE 是 99%)

按下 KEY1 那一刻


一:vTaskGetRunTimeStats()— 任务运行时间统计

1:函数原型

void vTaskGetRunTimeStats( char *pcWriteBuffer );
形参含义
pcWriteBuffer输出缓冲区指针,写入格式化后的表格字符串。每个任务约需 40 字节

返值:无。数据写入pcWriteBuffer

2:需要的三个宏(缺一不可)

在哪设干什么
configGENERATE_RUN_TIME_STATS = 1FreeRTOSConfig.h开运行时间统计功能
configUSE_STATS_FORMATTING_FUNCTIONS = 1FreeRTOSConfig.h开格式化输出(昨天已开 ✅)
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()我们自己在 FreeRTOSConfig.h 实现初始化一个高精度时基定时器
portGET_RUN_TIME_COUNTER_VALUE()我们自己在 FreeRTOSConfig.h 实现读该定时器的当前计数值

3:输出格式

Task Abs Time % Time task2 12345 48% task1 10000 39% IDLE 3333 13% start_task 0 <1%
含义
Task任务名
Abs Time该任务自启动以来累计跑了多久(计时器 tick 数)
% Time占总 CPU 时间的百分比

4:跟uxTaskGetSystemState()的关系

vTaskGetRunTimeStats内部就是调了uxTaskGetSystemState(),然后把它返回的ulRunTimeCounter字段格式化成表格。核心逻辑(源码 tasks.c):

// 简化版内部实现 void vTaskGetRunTimeStats(char *pcWriteBuffer) { TaskStatus_t *pxTaskStatusArray; UBaseType_t uxArraySize; uint32_t ulTotalRunTime; ​ uxArraySize = uxTaskGetNumberOfTasks(); pxTaskStatusArray = pvPortMalloc(uxArraySize * sizeof(TaskStatus_t)); ​ if (pxTaskStatusArray != NULL) { // ★ uxTaskGetSystemState 第三个参数传 &ulTotalRunTime(不是 NULL 了!) uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, &ulTotalRunTime); ​ // 用 sprintf 格式化每个任务的百分比 for (x = 0; x < uxArraySize; x++) { ulStatsAsPercentage = pxTaskStatusArray[x].ulRunTimeCounter / ulTotalRunTime; sprintf(pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n", ...); } ​ vPortFree(pxTaskStatusArray); } }

5:时基定时器的要求

计时精度需高于系统时钟节拍精度的10 至 100 倍

我们 SysTick 是 1ms(1KHz),所以时基定时器至少10KHz ~ 100KHz。比如用 TIM6 配 10KHz = 0.1ms 精度。

6:跟昨天学的 API 对比:

vTaskListvTaskGetRunTimeStats
输出内容状态、优先级、栈剩余运行时间、CPU 占比
内部调用uxTaskGetSystemStateuxTaskGetSystemState
额外宏configUSE_STATS_FORMATTING_FUNCTIONS还要configGENERATE_RUN_TIME_STATS+ 两个 port 宏
需要外设需要一个硬件定时器(不能用 SysTick,它已经是心跳了)

二:最终改动清单 — rtos\8\ 任务运行时间统计实验


TIM6.h

行号内容
12-13新增RT_STATS_PSC(89)/RT_STATS_ARR(9)— 100KHz 时基宏
17新增void ConfigureTimeForRunTimeStats(void)声明
18新增extern uint32_t FreeRTOSRunTimeTicks全局计数器声明

TIM6.c

行号内容
7新增uint32_t FreeRTOSRunTimeTicks全局计数器定义
29-49新增ConfigureTimeForRunTimeStats()— 配 TIM6 为 100KHz(PSC=89, ARR=9)
56-60HAL_TIM_PeriodElapsedCallback里 TIM6 分支:FreeRTOSRunTimeTicks++

stm32f4xx_it.c

行号内容
172-178新增TIM6_DAC_IRQHandler()— ISR 入口,调HAL_TIM_IRQHandler(&htim6)

FreeRTOSConfig.h

行号内容
172configGENERATE_RUN_TIME_STATS0→1
173新增#include "../BSP/TIM6/TIM6.h"— 引入 ConfigureTimeForRunTimeStats 声明
174新增portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()ConfigureTimeForRunTimeStats()
175新增portGET_RUN_TIME_COUNTER_VALUE()FreeRTOSRunTimeTicks
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 这是一个 初始化函数宏。 当 FreeRTOS 启动调度器(vTaskStartScheduler())时,会自动调用这个宏。 它的职责是: 👉 初始化一个高精度定时器(这里是 TIM6),为任务时间统计做准备。 你需要在 ConfigureTimeForRunTimeStats() 里做什么? 通常需要完成以下几件事: 开启 TIM6 时钟 配置 TIM6 的预分频(PSC),让计数频率 远高于系统节拍 配置自动重装值(ARR),决定溢出周期 开启 TIM6 更新中断(可选,但推荐) 启动 TIM6 ​ void ConfigureTimeForRunTimeStats(void) { FreeRTOSRunTimeTicks = 0; /* 计数器归零 */ ​ TIM6_CLK_ENABLE(); /* 使能 TIM6 时钟 */ htim6.Instance = TIM6; htim6.Init.Prescaler = RT_STATS_PSC; /* 89 */ htim6.Init.CounterMode = TIM_COUNTERMODE_UP; htim6.Init.Period = RT_STATS_ARR; /* 9 */ HAL_TIM_Base_Init(&htim6); ​ HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 6, 0); /* 抢占 6,低于关键中断 */ HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn); ​ HAL_TIM_Base_Start_IT(&htim6); }
#define portGET_RUN_TIME_COUNTER_VALUE() FreeRTOSRunTimeTicks ​ 这是 获取当前“系统运行时间基准值”的宏。 FreeRTOS 在以下时刻会调用它: 任务切换时 系统节拍中断中 调用 vTaskGetRunTimeStats() 时 👉 它的返回值,代表“从系统启动到现在,一共经过了多少个高精度时间单位”。

main.c

行号内容
5新增#include "./Key/Key.h"— 按键模块
7新增#include "./TIM6/TIM6.h"— TIM6 运行时间统计
18新增ConfigureTimeForRunTimeStats()— 调度器启动前配好 TIM6
20新增Key_Init()— 初始化按键 GPIO
28printf 标题 →"FreeRTOS Run Time Stats Test!"

freertos_demo.c — 整个文件重写

行号内容
37task2 优先级=3(比 task1 高,方便中断响应)
46static char task_buff[512]vTaskGetRunTimeStats输出缓冲区
88-97task1:LED 红 500ms 交替闪烁
109-129task2:扫描 KEY1(PA0) → 按键触发vTaskGetRunTimeStats(task_buff)→ printf
132vTaskDelay(10)— 10ms 扫一次按键

Key.c

行号内容
2-3新增#include "FreeRTOS.h"/#include "task.h"— RTOS API
30HAL_Delay(10)vTaskDelay(10 / portTICK_PERIOD_MS)— 消抖不卡 CPU
33-34while(...);死循环 →while(...) vTaskDelay(...)— 等松开时让出 CPU

完整中断链路

TIM6 硬件 (100KHz=10μs) → 向量表 TIM6_DAC_IRQHandler [stm32f4xx_it.c:175] → HAL_TIM_IRQHandler(&htim6) → HAL_TIM_PeriodElapsedCallback [TIM6.c:54] → FreeRTOSRunTimeTicks++ [TIM6.c:58] ↓ FreeRTOS 内核 portGET_RUN_TIME_COUNTER_VALUE() 读数 ↓ vTaskGetRunTimeStats() → sprintf 格式化 → printf 输出

跟正点代码的对比

正点我们
时基TIM6,btim.c/btim.h专用模块TIM6,复用BSP/TIM6/加功能
时基初始化port宏在调度器里调main 里主动调 + port 宏双重保险
ISR 位置btim.cBTIM_TIMX_INT_IRQHandlerstm32f4xx_it.cTIM6_DAC_IRQHandler
按键触发key_scan(0)+ KEY0Key_Scan(PA0)+ KEY1,RTOS 版消抖
LEDLED0_TOGGLE()LED_R(0/1)红灯交替
临界区taskENTER/EXIT_CRITICAL包创建无临界区(之前验证过不需要)

三:rtos\8 任务运行时间轴

启动阶段

上电 → HAL_Init → SysTick_Init(180MHz) → TIM7_Init(HAL时基) → ConfigureTimeForRunTimeStats(TIM6 100KHz 开始计数) → LED_Init / Key_Init / USART1_Init → printf("FreeRTOS Run Time Stats Test!") → freertos_demo() ├── xTaskCreate(start_task, prio=1) └── vTaskStartScheduler() └── xPortStartScheduler → prvStartFirstTask → svc 0 → SVC → 第一个任务 start_task 开始跑!

start_task(优先级 1,活不过 1ms)

start_task 跑: ├── xTaskCreate(task1, prio=2) → task1 就绪 ├── xTaskCreate(task2, prio=3) → task2 就绪(比 task1 高!) └── vTaskDelete(NULL) → 自杀,触发 PendSV → 找到最高优先级 task2(prio=3) → 🎉 task2 开始跑

稳态运行(无限循环)

时间轴 → task2 (prio=3, 最高): Key_Scan(PA0) → 没按下 → vTaskDelay(10) → 睡 10ms ├─ 10ms 后醒来 ──→ Key_Scan → vTaskDelay(10) → 睡 ├─ 10ms 后醒来 ──→ ...(循环)... └─ 某次醒来 → KEY1 按下!→ vTaskGetRunTimeStats → printf(表格) → vTaskDelay(10) → 睡 task1 (prio=2): LED_R(0) 亮 → vTaskDelay(500) → 睡 500ms ├─ 500ms 后醒来 → LED_R(1) 灭 → vTaskDelay(500) → 睡 ├─ 500ms 后醒来 → LED_R(0) 亮 → vTaskDelay(500) → 睡 └─ ...(永远红灯亮 500ms / 灭 500ms)... IDLE (prio=0): 当 task1 和 task2 都在睡的时候跑 → 回收被删任务的尸体 → 空转省电

CPU 占用占比(为什么 IDLE 是 99%)

每 1 秒内 ──────────────────────────────────────────────── task2:每 10ms 醒来一次,读 GPIO + vTaskDelay,醒着时间 ≈ 几十 μs → 每 1 秒内实际干活 ≈ 0.01 秒 → 1% task1:每 500ms 醒来一次,LED 翻转 + vTaskDelay → 每 1 秒实际干活 ≈ 0.001 秒 → <1% IDLE:以上两个任务都睡了的 99% 时间 → IDLE 在空转

按下 KEY1 那一刻

task2 扫到按键按下 → delay_ms(10) 消抖(CPU 轮询,不切换任务) → 确认按下 → while(按下) 等松开(CPU 轮询) → 松开 → 返回 KEY_ON → vTaskGetRunTimeStats(task_buff) ├── pvPortMalloc → uxTaskGetSystemState(&ulTotalTime) │ └── ulTotalTime = FreeRTOSRunTimeTicks(TIM6 100KHz 累计值) ├── 算出每个任务占比 → sprintf 格式化 └── 写入 task_buff → printf("%s", task_buff) → 串口输出表格 → vTaskDelay(10) → 继续扫按键

核心就是:两个任务都在睡 → CPU 交给 IDLE → 99% 空闲率。这也证明了 FreeRTOS 的调度没有额外开销——不干活的时候 CPU 就真闲着。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询