FreeRTOS从入门到精通 第十章(FreeRTOS任务相关API函数)

参考教程:【正点原子】手把手教你学FreeRTOS实时系统_哔哩哔哩_bilibili

一、FreeRTOS任务相关API函数介绍

1、uxTaskPriorityGet函数

(1)此函数用于获取指定任务的任务优先级,使用该函数前需在FreeRTOSConfig.h文件中将宏INCLUDE_uxTaskPriorityGet置1。

(2)函数接口定义:

UBaseType_t uxTaskPriorityGet    //返回任务优先级数值
(
    const TaskHandle_t xTask     //要查找的任务句柄,NULL代表任务自身
)

2、vTaskPrioritySet函数

(1)此函数用于改变某个任务的任务优先级,使用该函数前需在FreeRTOSConfig.h文件中将宏INCLUDE_vTaskPrioritySet为1。

(2)函数接口定义:

void vTaskPrioritySet
(
    TaskHandle_t xTask ,             //要更改优先级的任务的任务句柄
    UBaseType_t uxNewPriority       //需要设置的任务优先级
)

3、uxTaskGetNumberOfTasks函数

(1)此函数用于获取系统中任务的任务数量。

(2)函数接口定义:

UBaseType_t uxTaskGetNumberOfTasks  //返回系统中任务的数量
(
    void
)

4、uxTaskGetSystemState函数

(1)此函数用于获取系统中所有任务的任务状态信息,使用该函数前需在FreeRTOSConfig.h文件中将宏configUSE_TRACE_FACILITY置1。

(2)函数接口定义:

UBaseType_t  uxTaskGetSystemState    //返回获取信息的任务数量
(
    //指向TaskStatus_t结构体数组首地址,用于接收所有任务的任务状态信息
    TaskStatus_t * const pxTaskStatusArray,
    //接收信息的数组的大小
    const UBaseType_t uxArraySize,
    //系统总运行时间,为NULL则省略总运行时间值
    configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime
)

(3)TaskStatus_t结构体定义:

typedef struct xTASK_STATUS
{
    TaskHandle_t 		xHandle;                       //任务句柄
    const char *		 	pcTaskName;                   //任务名
    UBaseType_t			xTaskNumber;                  //任务编号
    eTaskState e			CurrentState;                   //任务状态
    UBaseType_t 			uxCurrentPriority;               //任务优先级
    UBaseType_t 			uxBasePriority;                 //任务原始优先级
    configRUN_TIME_COUNTER_TYPE ulRunTimeCounter;  //任务运行时间
    StackType_t * 		pxStackBase;                   //任务栈基地址
    configSTACK_DEPTH_TYPE 	usStackHighWaterMark;  //任务栈历史剩余最小值
} TaskStatus_t;

5、vTaskGetInfo函数

(1)此函数用于获取指定的单个任务的状态信息,使用该函数前需在FreeRTOSConfig.h文件中将宏configUSE_TRACE_FACILITY置1。

(2)函数接口定义:

void vTaskGetInfo
(
    TaskHandle_t 	xTask,               //指定获取信息的任务的句柄
	TaskStatus_t * 	pxTaskStatus,         //接收任务信息的变量
	BaseType_t 		xGetFreeStackSpace,   //任务栈历史剩余最小值,当为“pdFALSE” 则跳过这个步骤,当为“pdTRUE”则检查历史剩余最小堆栈
	eTaskState 		eState    //任务状态,可直接赋值(不是改变任务状态,而是跳过获取步骤,直接把eState当作结果值),如想获取代入“eInvalid”
)

(3)eTaskState枚举定义:

typedef enum
{   
	eRunning = 0,		/* 运行态 */ 
	eReady			/* 就绪态 */ 
	eBlocked, 		/* 阻塞态 */ 
	eSuspended, 		/* 挂起态 */ 
	eDeleted, 		/* 任务被删除 */ 
	eInvalid			/* 无效 */ 
} eTaskState;

6、xTaskGetCurrentTaskHandle函数

(1)此函数用于获取当前任务的任务句柄,使用该函数前需在FreeRTOSConfig.h文件中将宏INCLUDE_xTaskGetCurrentTaskHandle置1。

(2)函数接口定义:

TaskHandle_t  xTaskGetCurrentTaskHandle    //返回当前任务的任务句柄
(
    void
)

7、xTaskGetHandle函数

(1)此函数用于通过任务名获取任务句柄,使用该函数前需在FreeRTOSConfig.h文件中将宏INCLUDE_xTaskGetHandle置1。

(2)函数接口定义:

TaskHandle_t  xTaskGetHandle    //返回任务名对应的任务句柄
(
    const char * pcNameToQuery  //任务名
)

8、uxTaskGetStackHighWaterMark函数

(1)此函数用于获取指定任务的任务栈历史最小剩余堆栈,使用该函数前需在FreeRTOSConfig.h文件中将宏INCLUDE_uxTaskGetStackHighWaterMark置1。

(2)函数接口定义:

UBaseType_t  uxTaskGetStackHighWaterMark    //返回任务栈的历史剩余最小值
(
    TaskHandle_t  xTask         //任务句柄
)

9、eTaskGetState函数

(1)此函数用于查询某个任务的运行状态,使用此函数前需在FreeRTOSConfig.h文件中将宏INCLUDE_eTaskGetState置1。

(2)函数接口定义:

eTaskState eTaskGetState          //返回任务状态
(
    TaskHandle_t xTask           //待获取状态任务的任务句柄
)

10、vTaskList函数

(1)此函数用于以“表格”的形式获取系统中任务的信息,使用此函数前需在FreeRTOSConfig.h文件中将宏configUSE_TRACE_FACILITY和configUSE_STATS_FORMATTING_FUNCTIONS置1。

(2)函数接口定义:

void  vTaskList
(
    char * pcWriteBuffer            //接收任务信息的缓存指针
)

(3)“表格”的属性如下所示:

①Name:创建任务的时候给任务分配的名字。
②State:任务的壮态信息,B是阻塞态,R是就绪态,S是挂起态,D是删除态。
③Priority:任务优先级。
④Stack:任务堆栈的“高水位线”,就是堆栈历史最小剩余大小。
⑤Num:任务编号,这个编号是唯一的,当多个任务使用同一个任务名的时候可以通过此编号来做区分。

11、vTaskGetRunTimeStats函数

(1)此函数用于统计任务的运行时间信息,该函数耗时较长,一般仅在调试阶段使用,使用此函数前需在FreeRTOSConfig.h文件中将宏configGENERATE_RUN_TIME_STATS、configUSE_STATS_FORMATTING_FUNCTIONS置1,并且还需要实现如下两个宏定义。

①portCONFIGURE_TIMER_FOR_RUN_TIME_STATS():用于初始化用于配置任务运行时间统计的时基定时器,这个时基定时器的计时精度需高于系统时钟节拍精度的10至100倍。

②portGET_RUN_TIME_COUNTER_VALUE():用于获取该功能时基硬件定时器计数的计数值。

(2)函数接口定义:

void  vTaskGetRunTimeStats
(
    char * pcWriteBuffer         //接收任务运行时间信息的缓存指针
)

(3)任务运行时间信息属性:

①Task:任务名称。

②Abs Time:任务实际运行的总时间(绝对时间)。

③% Time:占总处理时间的百分比(空闲任务的占比时间越大,说明CPU压力越小)。

二、任务状态查询API函数实验

1、原理图与实验目标

(1)原理图:

(2)实验目标:

①设计4个任务——start_task、task1、task2、task3:

[1]start_task:用于创建其它三个任务,然后删除自身。

[2]task1:实现LED1状态反转。

[3]task2:实现LED2状态反转。

[4]task3:用于展示任务状态信息查询相关API函数的使用。

②预期实验现象:

[1]程序下载到板子上后,两个LED灯闪烁。

[2]串口助手中有相关信息输出。

2、实验步骤

(1)将“列表项的插入和删除实验”的工程文件夹复制一份,在拷贝版中进行实验。

(2)在FreeRTOSConfig.h文件中将相关宏全部置1,具体见本章的函数介绍部分。

(3)移除FreeRTOS_experiment.c文件中的原内容,替换为如下内容。

#include "FreeRTOS.h"
#include "task.h"
#include "LED.h"
#include "Key.h"
#include "Serial.h"
#include "stdlib.h"

//宏定义
#define START_TASK_STACK_SIZE 128   //start_task任务的堆栈大小
#define START_TASK_PRIO       1     //start_task任务的优先级
#define TASK1_STACK_SIZE      128   //task1任务的堆栈大小
#define TASK1_PRIO            2     //task1任务的优先级
#define TASK2_STACK_SIZE     128   //task2任务的堆栈大小
#define TASK2_PRIO            3     //task2任务的优先级
#define TASK3_STACK_SIZE     128   //task3任务的堆栈大小
#define TASK3_PRIO            4     //task3任务的优先级

//任务函数声明
void start_task(void);
void task1(void);
void task2(void);
void task3(void);

//任务句柄
TaskHandle_t start_task_handler;    //start_task任务的句柄
TaskHandle_t task1_handler;         //task3任务的句柄
TaskHandle_t task2_handler;         //task3任务的句柄
TaskHandle_t task3_handler;         //task3任务的句柄

void FreeRTOS_Test(void)
{
	//创建任务start_task
	xTaskCreate((TaskFunction_t)start_task,            //指向任务函数的指针
				"start_task",                       //任务名字
				START_TASK_STACK_SIZE,        //任务堆栈大小,单位为字
				NULL,                           //传递给任务函数的参数
				START_TASK_PRIO,               //任务优先级
				(TaskHandle_t *) &start_task_handler  //任务句柄,就是任务的任务控制块
				);
	
	//开启任务调度器
	vTaskStartScheduler();
}

void start_task(void)
{
	taskENTER_CRITICAL();      //进入临界区
	
	//创建任务task1
	xTaskCreate((TaskFunction_t)task1,             //指向任务函数的指针
				"task1",                        //任务名字
				TASK1_STACK_SIZE,           //任务堆栈大小,单位为字
				NULL,                        //传递给任务函数的参数
				TASK1_PRIO,                  //任务优先级
				(TaskHandle_t *) &task1_handler   //任务句柄,就是任务的任务控制块
				);
	
	//创建任务task2
	xTaskCreate((TaskFunction_t)task2,            //指向任务函数的指针
				"task2",                       //任务名字
				TASK2_STACK_SIZE,          //任务堆栈大小,单位为字
				NULL,                       //传递给任务函数的参数
				TASK2_PRIO,                 //任务优先级
				(TaskHandle_t *) &task2_handler  //任务句柄,就是任务的任务控制块
				);
	
	//创建任务task3
	xTaskCreate((TaskFunction_t)task3,            //指向任务函数的指针
				"task3",                        //任务名字
				TASK3_STACK_SIZE,           //任务堆栈大小,单位为字
				NULL,                         //传递给任务函数的参数
				TASK3_PRIO,                   //任务优先级
				(TaskHandle_t *) &task3_handler    //任务句柄,就是任务的任务控制块
				);
	
	vTaskDelete(NULL);        //删除任务自身
	
	taskEXIT_CRITICAL();      //退出临界区
}

void task1(void)
{
	while(1)
	{
		LED1_Turn();     //LED1状态翻转
		vTaskDelay(500);  //延时(自我阻塞)500ms
	}
}

void task2(void)
{
	while(1)
	{
		LED2_Turn();     //LED2状态翻转
		vTaskDelay(500);  //延时(自我阻塞)500ms
	}
}

char task_buff[500];
void task3(void)
{
	UBaseType_t priority_num = 0;
    UBaseType_t task_num = 0;
    UBaseType_t task_num2 = 0;
    TaskStatus_t * status_array = NULL;
    TaskStatus_t * status_array2 = NULL;
    TaskHandle_t task_handle = 0;
    //UBaseType_t task_stack_min = 0;
    eTaskState state = eRunning;
    uint8_t i = 0;
    
    vTaskPrioritySet(task2_handler,4);
    priority_num = uxTaskPriorityGet(NULL);
    Serial_Printf("task2任务优先级为%ld\r\n",priority_num);
    
    task_num = uxTaskGetNumberOfTasks();
    Serial_Printf("任务数量:%ld\r\n",task_num);
    
    status_array = malloc((sizeof(TaskStatus_t) * task_num));
    task_num2 = uxTaskGetSystemState(status_array,task_num,NULL);
    Serial_Printf("任务名\t\t任务优先级\t任务编号\r\n");
    for(i = 0; i < task_num2; i++)
    {
        Serial_Printf("%s\t\t%ld\t%ld\r\n",status_array[i].pcTaskName,
                status_array[i].uxCurrentPriority,status_array[i].xTaskNumber);
    }
    
    status_array2 = malloc(sizeof(TaskStatus_t));
    vTaskGetInfo( task2_handler,status_array2,pdTRUE,eInvalid);
    Serial_Printf("任务名:%s\r\n",status_array2->pcTaskName);
    Serial_Printf("任务优先级:%ld\r\n",status_array2->uxCurrentPriority);
    Serial_Printf("任务编号:%ld\r\n",status_array2->xTaskNumber);
    Serial_Printf("任务状态:%d\r\n",status_array2->eCurrentState);
    
    task_handle = xTaskGetHandle("task1");
    Serial_Printf("任务句柄:%#x\r\n",(int)task_handle);
    Serial_Printf("task1的任务句柄:%#x\r\n",(int)task1_handler);
    
    state = eTaskGetState(task2_handler);
    Serial_Printf("当前task2的任务状态为:%d\r\n",state);
    
    vTaskList(task_buff);
    Serial_SendString(task_buff);
    while(1)
    {
        //task_stack_min = uxTaskGetStackHighWaterMark(task2_handler);
        //Serial_Printf("task2历史剩余最小堆栈为%ld\r\n",task_stack_min);
        vTaskDelay(1000);
    }
}

(4)程序完善好后点击“编译”,然后将程序下载到开发板上,打开串口助手,观察串口中的内容。

3、程序执行流程

(1)main函数全流程:

①初始化OLED模块、按键模块、LED模块(还有串口模块,下图未示出)。

②调用FreeRTOS_Test函数。

(2)测试函数全流程:

①创建任务start_task。

②开启任务调度器。

(3)多任务调度执行阶段(发生在开启任务调度器以后):

①start_task任务函数首先进入临界区,在临界区中start_task任务不会被其它任务打断(否则创建task1后,由于task1的优先级较高,start_task任务会被打断),接着start_task任务依次创建任务task1、task2、task3,然后删除自身,接着退出临界区,让出CPU资源。

②task3的任务优先级最高,优先执行task3,task3将向串口打印一大串信息,如下所示,具体可根据代码一一对照,包括task1和task2的执行情况,这里也不再赘述。

三、任务时间统计API函数实验

1、原理图与实验目标

(1)原理图:

(2)实验目标:

①设计4个任务——start_task、task1、task2、task3:

[1]start_task:用于创建其它三个任务,然后删除自身。

[2]task1:实现LED1状态反转。

[3]task2:实现LED2状态反转。

[4]task3:用于展示任务运行时间统计相关API函数的使用。

②预期实验现象:

[1]程序下载到板子上后,两个LED灯闪烁。

[2]按下按键,串口助手中有任务执行时间的相关信息输出。

2、实验步骤

(1)将“任务状态查询API函数实验”的工程文件夹复制一份,在拷贝版中进行实验。

(2)将“FreeRTOS中断管理实验”工程文件夹中的Timer.c和Timer.h文件拷贝至本实验工程中,如下图所示。

(3)在FreeRTOSConfig.h文件中将宏configGENERATE_RUN_TIME_STATS、configUSE_STATS_FORMATTING_FUNCTIONS置1,并且还需要实现如下两个宏定义。

#define configGENERATE_RUN_TIME_STATS          	1
#define configUSE_STATS_FORMATTING_FUNCTIONS  	1

#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()   ConfigureTimeForRunTimeStats()

extern uint32_t FreeRTOSRunTimeTicks;
#define portGET_RUN_TIME_COUNTER_VALUE()         FreeRTOSRunTimeTicks

(4)在Timer.c文件中定义ConfigureTimeForRunTimeStats函数,用于初始化用于配置任务运行时间统计的时基定时器,这个时基定时器的计时精度需高于系统时钟节拍精度的10至100倍,在本实验中系统节拍为1ms,则这个时基定时器的计时精度取10us(本实验使用TIM3,其时钟频率为90MHz,则预分频系数可取90,重装载值可取10)。(函数要在头文件中声明,这里不再赘述)

void Timer3_Init(void)
{
	/*开启时钟,配置时钟源*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//开启TIM3的时钟
	TIM_InternalClockConfig(TIM3);		//选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;			//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;	//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;		//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 90 - 1;		//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;	//重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);		//将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元	
	
	/*中断输出配置*/
	TIM_ClearFlag(TIM3, TIM_FLAG_Update);			//清除定时器更新标志位	
	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);		//开启TIM3的更新中断
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;				//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;	//选择配置NVIC的TIM3线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;	//指定NVIC线路的抢占优先级为6
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	//指定NVIC线路的响应优先级为0
	NVIC_Init(&NVIC_InitStructure);   //将结构体变量交给NVIC_Init,配置NVIC外设
	
	/*TIM使能*/
	TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定时器开始运行
}

uint32_t FreeRTOSRunTimeTicks;
void ConfigureTimeForRunTimeStats(void)
{
	Timer3_Init();
	FreeRTOSRunTimeTicks = 0;
}

(5)更改task3任务函数的实现。

void task3(void)
{
	uint8_t key = 0;
	
    while(1)
    {
		key = Key_GetNum();
		if(key == 1)           //按下一次按键,打印一次信息
		{
			vTaskGetRunTimeStats(task_buff);
			Serial_SendString(task_buff);
			Serial_SendString("\r\n------------------------\r\n");
		}
        vTaskDelay(10);
    }
}

(6)在main.c文件中实现TIM3定时器中断的服务函数。

void TIM3_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
	{
		FreeRTOSRunTimeTicks++;      //计数值自增
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
	}
}

(7)程序完善好后点击“编译”,然后将程序下载到开发板上,打开串口助手分析信息。

3、程序执行流程

(1)main函数全流程:

①初始化OLED模块、按键模块、LED模块(还有串口模块,下图未示出)。

②调用FreeRTOS_Test函数。

(2)测试函数全流程:

①创建任务start_task。

②开启任务调度器。

(3)多任务调度执行阶段(发生在开启任务调度器以后):

①start_task任务函数首先进入临界区,在临界区中start_task任务不会被其它任务打断(否则创建task1后,由于task1的优先级较高,start_task任务会被打断),接着start_task任务依次创建任务task1、task2、task3,然后删除自身,接着退出临界区,让出CPU资源。

②task3的任务优先级最高,优先执行task3,每次在task3中按下按键,单片机将向串口打印一大串信息,如下所示,具体可根据代码一一对照,包括task1和task2的执行情况,这里也不再赘述。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevalin爱灰灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值