一:任务时间统计理论基础
二:代码部分
1、宏定义修改
2、应用程序还必须提供 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 和 portGET_RUN_TIME_COUNTER_VALUE 的定义, 以分别配置外设的定时器/计数器和返回定时器的当前计数值。 计数器 的频率应该至少是 tick 计数的 10 倍。
如果不定义函数,出现报错
定义完上面两个宏定义之后,在configfreertos.h中定义下列宏
注意:如果仅仅定义上面这些,依然会报错,说找不到这个函数vTaskGetRunTimeStats(WriteBuffer) ;我们在task.c文件中看看这个函数,如下:
#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configUSE_TRACE_FACILITY == 1 ) )
void vTaskGetRunTimeStats( char * pcWriteBuffer )
{
TaskStatus_t * pxTaskStatusArray;
UBaseType_t uxArraySize, x;
configRUN_TIME_COUNTER_TYPE ulTotalTime, ulStatsAsPercentage;
他的if里面有三个宏定义要求,我们只定义了两个,所以找不到函数,为了方便,后续我们不在freertos.h中修改宏定义,将宏定义全部定义在configfreertos.h文件中:如下
extern uint32_t VALUE;
#define portGET_RUN_TIME_COUNTER_VALUE() VALUE
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() TIMER_FOR_RUN_TIME_STATS()
#define configGENERATE_RUN_TIME_STATS 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
#define configUSE_TRACE_FACILITY 1
完整代码:
key.h
#ifndef __KEY_H
#define __KEY_H
void Key_Init(void);
uint8_t Key_GetNum(void);
#endif
key.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "FreeRTOS.h"
#include "task.h"
#include "Serial.h"
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
uint8_t Key_GetNum(void)
{
taskENTER_CRITICAL();
uint8_t KeyNum = 0;
//当按键按下时,为低电平,进入if,因为是上拉模式,按下为低电平,松手为高电平
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
//按下的过程就像弹簧压缩一样,防止震荡,加个延时函数
vTaskDelay(10);
//松手时为上拉状态,此时为高电平,退出循环,
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
//防止震荡
vTaskDelay(10);
//给按键赋值
KeyNum = 1;
}
taskEXIT_CRITICAL();
return KeyNum;
}
timer.h
#ifndef __TIMER_H
#define __TIMER_H
//void Timer_Init(void);
void Timer_Init(uint16_t TIM_Period,uint16_t TIM_Prescaler);
void TIMER_FOR_RUN_TIME_STATS(void);
#endif
timer.c
#include "stm32f10x.h" // Device header
#include "Serial.h"
#include "FreeRTOS.h"
#include "task.h"
uint32_t VALUE;
void Timer_Init(uint16_t TIM_Period,uint16_t TIM_Prescaler)
{
//RCC打开时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//选择时基单元的时钟,内部时钟一般默认初始化可以写可以不写
TIM_InternalClockConfig(TIM2);
//配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
//TIM_CKD_DIV1代表1分屏
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
//代表向上计数
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = TIM_Period;
//72MHZ分频7200,就是10k,10k计10000个数就是1s
TIM_TimeBaseInitStructure.TIM_Prescaler = TIM_Prescaler;
//高级定时器才有,现在是通用定时器给0
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
//如果不加入这一句,会导致复位之后从1开始计数
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
//TIM_IT_Update代表更新中断,中断控制,用来控制某个中断能不能通往NIVC
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
//启动定时器
TIM_Cmd(TIM2, ENABLE);
}
void TIMER_FOR_RUN_TIME_STATS(void)
{
Timer_Init(10-1,72-1);
VALUE=0;
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
VALUE++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
#include "OLED.h"
#include "Serial.h"
#include "FreeRTOS.h"
#include "task.h"
#include "Timer.h"
uint16_t Num=0;
char WriteBuffer[500];
#define START_STK_DEPTH 128
#define START_TASK_PRIO 1
TaskHandle_t start_task_handler;
void start_task(void * pvParameters);
#define LED2_STK_DEPTH 128
#define LED2_TASK_PRIO 2
TaskHandle_t led2_task_handler;
void led2_task(char * pvParameters);
#define LED3_STK_DEPTH 128
#define LED3_TASK_PRIO 3
TaskHandle_t led3_task_handler;
void led3_task(char * pvParameters);
int main(void)
{
LED_Init();
Serial_Init();
Key_Init();
xTaskCreate( (TaskFunction_t) start_task, //创建开始任务
(const char * ) "start_task",
(uint16_t ) START_STK_DEPTH,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(TaskHandle_t *) &start_task_handler);
vTaskStartScheduler(); //开启任务调度器
}
/*******开始任务函数*****/
void start_task(void * pvParameters)
{
taskENTER_CRITICAL();
xTaskCreate((TaskFunction_t) led2_task,
(const char * ) "led2_task",
(uint16_t ) LED2_STK_DEPTH,
(void * ) NULL,
(UBaseType_t ) LED2_TASK_PRIO,
(TaskHandle_t *) &led2_task_handler);
xTaskCreate((TaskFunction_t) led3_task,
(const char * ) "led3_task",
(uint16_t ) LED3_STK_DEPTH,
(void * ) NULL,
(UBaseType_t ) LED3_TASK_PRIO,
(TaskHandle_t *) &led3_task_handler);
vTaskDelete(start_task_handler);
taskEXIT_CRITICAL();
}
void led2_task(char * pvParameters)
{
for(;;)
{
LED1_Turn();
vTaskDelay(200);
}
}
void led3_task(char * pvParameters)
{
for(;;)
{
Num=Key_GetNum();
if(Num==1)
{
vTaskGetRunTimeStats(WriteBuffer) ;
Serial_Printf("%s\r\n",WriteBuffer);
}
vTaskDelay(10);
}
}
注意:我们这里的任务堆栈大小为128,我之前设置的64,但是我发现设置64在实际操作中会出现错误,例如:我第一次按下按键,能够正常显示任务时间,第二次按下led灯闪烁那个任务会死机,后面怎么按都无效,现在改为128就可以正常运行了。
实验结果: