统计任务信息
1,目的:可以通过查看任务的CPU情况、使用栈情况,进行针对优化
2,栈使用情况:由于创建任务时候,栈固定的填充了0xa5,以后可以通过使用下面函数查看栈的高水位,也就是剩余栈空间
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );
原理是:从栈底往栈顶逐个字节地判断,它们的值持续是0xa5就表示它是空闲的。
3,任务运行时间统计:需要比tick更快的始终,比如tick周期1ms,选择0.1ms的定时器
- 切换task1时候,使用更快的定时器记录当前时间T1
- task1被切出去时,使用更快的定时器记录当前时间T4
- T4-T1 就是运行时间,累加起来。
代码学习步骤
1,在main.c包含更高timer的相关配置
#include "timer.h"
/*main.c 创建两个任务*/
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
/*新建两个任务*/
char pcWriteBuffer[200];
void Task1Function(void * param)
{
volatile int i = 0;
while (1)
{
vTaskGetRunTimeStats(pcWriteBuffer);/*获取vTaskGetRunTimeStats running信息*/
printf(pcWriteBuffer);/*打印*/
vTaskDelay(5000);
}
}
void Task2Function(void * param)
{
volatile int i = 0;
while (1)
{
}
}
在启动调度文件vTaskStartScheduler()中,包含了代码:
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
启动更高定时器
在FreeRTOSConfig.h 文件中
添加宏定义
extern void TimerInit(void);
extern unsigned int TimerGetCount(void);
#define configGENERATE_RUN_TIME_STATS 1
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS TimerInit
#define portGET_RUN_TIME_COUNTER_VALUE TimerGetCount
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
extern void TimerInit(void);
#define configGENERATE_RUN_TIME_STATS 1
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS TimerInit
这3条宏定义,作用是将vTaskStartScheduler 开启调度函数中的
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS 定义为TimerInit
即在开启调度函数中调用定时器初始化函数。
运行路径:
TimerInit<=#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS TimerInit
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS<=vTaskStartScheduler
extern unsigned int TimerGetCount(void);
#define portGET_RUN_TIME_COUNTER_VALUE TimerGetCount
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
在timer.c中定义了一个全局变量,在定时器中断中,自增
#include "timer.h"
#include "stm32f10x_tim.h"
NVIC_InitTypeDef gTIMx_NVIC_Init; // 定义一个中断对象结构体
TIM_TimeBaseInitTypeDef gTIMX_TimeBase_Init; // 定义一个定时器对象结构体
static vu8 test_cnt = 0;
static unsigned int g_timer_cnt;
void TimerInit(void)
{
/* 1. 使能时钟 */
TIMx_Clk_Enable();
/* 2. 配置定时器对象结构体的成员,设置定时器属性 */
gTIMX_TimeBase_Init.TIM_Period = TIMx_PRE_LOAD_VALUE; // 设置预装载值,定时器会向上从0计数到value或者向下从value计数到0
gTIMX_TimeBase_Init.TIM_Prescaler = TIMx_PRESCALER; // 设置预分频系数
gTIMX_TimeBase_Init.TIM_ClockDivision = TIMx_DIV; // 设置分频系数
gTIMX_TimeBase_Init.TIM_CounterMode = TIM_CounterMode_Up; // 设置计数方式:Up/Down
/* 3. 调用库函数初始化定时器 */
TIM_TimeBaseInit( TIMx, &gTIMX_TimeBase_Init );
/* 4. 配置中断的属性 */
gTIMx_NVIC_Init.NVIC_IRQChannel = TIMx_IRQ; // 选择中断
gTIMx_NVIC_Init.NVIC_IRQChannelSubPriority = TIMx_NVIC_SUB_PRIORITY; // 设置中断的子优先级
gTIMx_NVIC_Init.NVIC_IRQChannelPreemptionPriority = TIMx_NVIC_PRE_PRIORITY; // 设置中断的抢占优先级
gTIMx_NVIC_Init.NVIC_IRQChannelCmd = ENABLE; // 使能中断
/* 5. 初始化中断 */
NVIC_Init( &gTIMx_NVIC_Init );
/* 6. 选择定时器的中断触发方式 */
TIM_ITConfig( TIMx, TIM_IT_Update, ENABLE ); // 选择为更新触发中断,即向上计数到预装载值或者向下技术到0触发中断
/* 7. 使能定时器 */
TIM_Cmd( TIMx, ENABLE );
}
u32 Test_GetCount(void)
{
return test_cnt;
}
u32 TimerGetCount(void)
{
return g_timer_cnt;
}
void TIMx_IRQHandler(void)
{
if( TIM_GetITStatus(TIMx, TIM_IT_Update) == SET ) // 判断是否是更新触发中断
{
test_cnt = !test_cnt;
g_timer_cnt++;
TIM_ClearITPendingBit( TIMx, TIM_IT_Update ); // 清除中断
}
}
timer.h
#define __TIMER_H
#include "stm32f10x_lib.h"
#define SYS_FREQ (72000000) // 系统频率
#define TIMx TIM3 // 目标定时器
#define TIMx_PERIOD (100-1) // 定时器的周期,单位:us
#define TIMx_PRE_LOAD_VALUE (1024-1) // 定时器的计数预装载值,范围:0~65535
#define TIMx_PRESCALER (SYS_FREQ/1000000*TIMx_PERIOD/TIMx_PRE_LOAD_VALUE) // 定时器预分频系数,范围:0~65535
#define TIMx_DIV (TIM_CKD_DIV1)
#define TIMx_Clk_Enable() RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3, ENABLE ) // 目标定时器的时钟使能
#define TIMx_IRQ TIM3_IRQChannel // 目标中断
#define TIMx_NVIC_PRE_PRIORITY (0) // 中断的抢占优先级等级
#define TIMx_NVIC_SUB_PRIORITY (1) // 中断的子优先级等级
#define TIMx_IRQHandler TIM3_IRQHandler
extern void TimerInit(void);
extern u32 Test_GetCount(void);
#endif /* __TIMER_H */
gTIMX_TimeBase_Init.TIM_Prescaler= TIMx_PRESCALER=(SYS_FREQ/1000000*TIMx_PERIOD/TIMx_PRE_LOAD_VALUE)
=(72000000/1000000*99/1023) = 7
中断时间 = ((1023 + 1) * (7 + 1)) / 72000000 = 0.1ms
extern unsigned int TimerGetCount(void); //获取定时器中断的计数值,这里0.1ms进一次中断计数
#define portGET_RUN_TIME_COUNTER_VALUE TimerGetCount
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
portGET_RUN_TIME_COUNTER_VALUE《=
uxTaskGetSystemState《=
void vTaskList( char * pcWriteBuffer )
最终通过vTaskList 将计数值 保持到pcWriteBuffer
在task1任务中,将vTaskList(pcWriteBuffer); 中的字符串打印出来(运行时间)
void Task1Function(void * param)
{
volatile int i = 0;
//xTimerStart(xMyTimerHandle, 0);
while (1)
{
//printf("Task1Function ...\r\n");
vTaskList(pcWriteBuffer);
vTaskGetRunTimeStats(pcWriteBuffer);
printf(pcWriteBuffer);
vTaskDelay(5000);
}
}
对于:vTaskGetRunTimeStats
vTaskGetRunTimeStats=》
打印运行时间占比
新代码使用步骤
1,添加定时器的代码
timer.c
timer.h
2,在FreeRTOSConfig.h添加宏定义,及声明函数
extern void TimerInit(void);
extern unsigned int TimerGetCount(void);
#define configGENERATE_RUN_TIME_STATS 1
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS TimerInit
#define portGET_RUN_TIME_COUNTER_VALUE TimerGetCount
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
3,在task中添加测试代码
char pcWriteBuffer[200];
void Task1Function(void * param)
{
volatile int i = 0;
while (1)
{
//vTaskList(pcWriteBuffer);
vTaskGetRunTimeStats(pcWriteBuffer);
printf(pcWriteBuffer);
vTaskDelay(100);
}
}
void Task1Function(void * param)
{
volatile int i = 0;
char pcWriteBuffer[200] = {0};
//xTimerStart(xMyTimerHandle, 0);
while (1)
{
#if 1
vTaskList(pcWriteBuffer);
//vTaskGetRunTimeStats(pcWriteBuffer);
printf(pcWriteBuffer);
vTaskDelay(100);
#endif
}
}