FreeRtos_任务管理学习记录

在 FreeRTOS 中,每个执行线程都被称为”任务”。在嵌入式社区中,对此并没有一
个公允的术语,但我更喜欢用”任务”而不是”线程”,因为从以前的经验来看,线程具有
更多的特定含义。

任务函数

任务是由 C 语言函数实现的。唯一特别的只是任务的函数原型,其必须返回 void,
而且带有一个 void 指针参数。其函数原型参见程序清单 1。

void ATaskFunction( void *pvParameters );//这个东西就相当于c语言的函数,这个名字是可以自己定义的

每个任务都是在自己权限范围内的一个小程序。其具有程序入口,通常会运行在一
个死循环中,也不会退出。
FreeRTOS 任务不允许以任何方式从实现函数中返回——它们绝不能有一
条”return”语句,也不能执行到函数末尾。

一个任务函数可以用来创建若干个任务——创建出的任务均是独立的执行实例,拥
有属于自己的栈空间,以及属于自己的自动变量(栈变量),即任务函数本身定义的变量。

void ATaskFunction( void *pvParameters ) 
{ 
/* 可以像普通函数一样定义变量。用这个函数创建的每个任务实例都有一个属于自己的iVarialbleExample变
量。但如果iVariableExample被定义为static,这一点则不成立 – 这种情况下只存在一个变量,所有的任务实
例将会共享这个变量。 */ 
int iVariableExample = 0; 
/* 任务通常实现在一个死循环中。等同while(1) */ 
for( ;; ) 
{ 
/* 完成任务功能的代码将放在这里。 */ 
} 
/* 如果任务的具体实现会跳出上面的死循环,则此任务必须在函数运行完之前删除。传入NULL参数表示删除
的是当前任务 */ 
vTaskDelete( NULL ); //此处的参数NULL表示删除自己,在这加入其他任务句柄,就可以删除其他任务(函数)了。
}

xTaskCreate() API 函数

portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode, 
const signed portCHAR * const pcName, 
unsigned portSHORT usStackDepth, 
void *pvParameters, 
unsigned portBASE_TYPE uxPriority, 
xTaskHandle *pxCreatedTask );//函数原型,这里面的各个参数在下面实例中会写到

pvTaskCode 	任务只是永不退出的 C 函数,实现常通常是一个死循环。参数
            pvTaskCode 只一个指向任务的实现函数的指针(效果上仅仅是函数名)。
pcName 		具有描述性的任务名。这个参数不会被 FreeRTOS 使用。其只是单
			纯地用于辅助调试。识别一个具有可读性的名字总是比通过句柄来识别容易得多。
			应用程序可以通过定义常量 config_MAX_TASK_NAME_LEN 来定
			义任务名的最大长度——包括’\0’结束符。如果传入的字符串长度超
			过了这个最大值,字符串将会自动被截断
usStackDepth 	当任务创建时,内核会分为每个任务分配属于任务自己的唯一状态。
				usStackDepth 值用于告诉内核为它分配多大的栈空间。
				这个值指定的是栈空间可以保存多少个字(word),而不是多少个字
				节(byte)。比如说,如果是 32 位宽的栈空间,传入的 usStackDepth
				值为 100,则将会分配 400 字节的栈空间(100 * 4bytes)。栈深度乘
				以栈宽度的结果千万不能超过一个 size_t 类型变量所能表达的最大值。
				应用程序通过定义常量 configMINIMAL_STACK_SIZE 来决定空闲
				任务任用的栈空间大小。在 FreeRTOS 为微控制器架构提供的
				Demo 应用程序中,赋予此常量的值是对所有任务的最小建议值。
				如果你的任务会使用大量栈空间,那么你应当赋予一个更大的值。
				没有任何简单的方法可以决定一个任务到底需要多大的栈空间。计
				算出来虽然是可能的,但大多数用户会先简单地赋予一个自认为合
				理的值,然后利用 FreeRTOS 提供的特性来确证分配的空间既不欠
				缺也不浪费。第六章包括了一些信息,可以知道如何去查询任务使
				用了多少栈空间。
pvParameters 	任务函数接受一个指向 void 的指针(void*)。pvParameters 的值即
				是传递到任务中的值。这篇文档中的一些范例程序将会示范这个参
				数可以如何使用。
uxPriority 		指定任务执行的优先级。优先级的取值范围可以从最低优先级 0 到
				最高优先级(configMAX_PRIORITIES – 1)。
				configMAX_PRIORITIES 是一个由用户定义的常量。优先级号并没
				有上限(除了受限于采用的数据类型和系统的有效内存空间),但最
				好使用实际需要的最小数值以避免内存浪费。如果 uxPriority 的值
				超过了(configMAX_PRIORITIES – 1),将会导致实际赋给任务的优
				先级被自动封顶到最大合法值。
pxCreatedTask 	pxCreatedTask 用于传出任务的句柄。这个句柄将在 API 调用中对
				该创建出来的任务进行引用,比如改变任务优先级,或者删除任务。
				如果应用程序中不会用到这个任务的句柄,则 pxCreatedTask 可以
				被设为 NULL。
返回值 			有两个可能的返回值:
				1. pdTRUE 
				表明任务创建成功。
				2. errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY 
				由于内存堆空间不足,FreeRTOS 无法分配足够的空间来保存任务
				结构数据和任务栈,因此无法创建任务。

demo_test1

void Vtask1(void *pvParameters)
{
    const char *pcTaskName = "task 1 running";
    for(;;){
        vprintString(pcTaskName);
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++){
            /*
                这里不做任何操作,相当于一个延时函数,。后面的示例程序将采用
			   delay/sleep函数代替这个原始空循环
            */

        }
    }

}

void Vtask2(void *pvParameters)
{
    const char *pcTaskName = "task 2 running";
    for(;;){
        vprintString(pcTaskName);
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++){
            /*
                这里不做任何操作,相当于一个延时函数,。后面的示例程序将采用
			   delay/sleep函数代替这个原始空循环
            */

        }
    }
}

int main (void)
{
    xTaskCreat(Vtask1,"Vtask1",configMINIMAL_STACK_SIZE * 4,NULL,configMAX_PRIORITIES - 1,NULL);
    xTaskCreat(Vtask2,"Vtask2",configMINIMAL_STACK_SIZE * 4,NULL,configMAX_PRIORITIES - 1,NULL);
    /*
        参数1:函数名
        参数2:“函数名”,仅在调试中用到
        参数3:栈的深度,他的大小是字,而不是字节,所以需要*4(一个字节4byte)
        参数4:没有任务参数
        参数5:任务优先级,这个宏-1(configMAX_PRIORITIES - 1)表示处于最高优先级
        参数6:任务句柄--任务句柄,在FreeRTOS又叫任务控制块,包含创建任务的所有信息,所有删除任务就可以这样来做
                        说明任务句柄包含创建任务的所有状态,说成任务的身份证最为合适,想删除,直接抹掉身份证就可以了
    */
	/* 启动调度器,任务开始执行 */
	vTaskStartScheduler();//这边在我理解看来就就相当于linux的线程等待函数,就是那个join
	/* 如果一切正常, main()函数不应该会执行到这里。但如果执行到这里,很可能是内存堆空间不足导致空闲
	   任务无法创建。第五章有讲述更多关于内存管理方面的信息 */
	for( ;; );   

}

在这里插入图片描述

任务优先级

xTaskCreate() API 函数的参数 uxPriority 为创建的任务赋予了一个初始优先级。这
个侁先级可以在调度器启动后调用 vTaskPrioritySet() API 函数进行修改。
应用程序在文件 FreeRTOSConfig.h 中设定的编译时配置常量
configMAX_PRIORITIES 的值,即是最多可具有的优先级数目。FreeRTOS 本身并没
有限定这个常量的最大值,但这个值越大,则内核花销的内存空间就越多。所以总是建
议将此常量设为能够用到的最小值。
调度器保证总是在所有可运行的任务中选择具有最高优先级的任务,并使其进入运行态。如果被选中的优先级上具有不止一个任务,调度器会让这些任务轮流执行。
要能够选择下一个运行的任务,调度器需要在每个时间片的结束时刻运行自己本身。一个称为心跳(tick,有些地方被称为时钟滴答,本文中一律称为时钟心跳)中断的周期性中断用于此目的。时间片的长度通过心跳中断的频率进行设定,心跳中断频率由FreeRTOSConfig.h 中的编译时配置常量 configTICK_RATE_HZ 进行配置。比如说,如果 configTICK_RATE_HZ 设为 100(HZ),则时间片长度为 10ms。将调度器本身的执行时间在整个执行流程中体现出来。
需要说明的是,FreeRTOS API 函数调用中指定的时间总是以心跳中断为单位(通
常的提法为心跳”ticks”)。常量 portTICK_RATE_MS(把这个当成1就行了) 用于将以心跳为单位的时间值转化为以毫秒为单位的时间值。有效精度依赖于系统心跳频率。

static const char *pcTextForTask1 = “Task 1 is running\r\n”; 
static const char *pcTextForTask2 = “Task 2 is running\t\n”; 
int main( void ) 
{ 
/* 第一个任务创建在优先级1上。优先级是倒数第二个参数。 */ 
xTaskCreate( vTaskFunction, "Task 1", 1000, (void*)pcTextForTask1, 1, NULL ); 
/* 第二个任务创建在优先级2上。 */ 
xTaskCreate( vTaskFunction, "Task 2", 1000, (void*)pcTextForTask2, 2, NULL ); 
/* Start the scheduler so the tasks start executing. */ 
vTaskStartScheduler(); 
return 0; 
}

任务函数与上文相同,由于任务2的优先级高于任务1所以只有任务2会运行
在这里插入图片描述
调度器总是选择所有能够进入运行态的任务中具有最高优先级的任务。一个高优先级但
不能够运行的任务意味着不会被调度器选中,而代之以另一个优先级虽然更低但能够运
行的任务。因此,采用事件驱动任务的意义就在于任务可以被创建在许多不同的优先级
上,并且最高优先级任务不会把所有的低优先级任务饿死。
(任务1就被饿死了)

阻塞状态

如果一个任务正在等待某个事件,则称这个任务处于”阻塞态(blocked)”。阻塞态是
非运行态的一个子状态。
任务可以进入阻塞态以等待以下两种不同类型的事件:

  1. 定时(时间相关)事件——这类事件可以是延迟到期或是绝对时间到点。比如
    说某个任务可以进入阻塞态以延迟 10ms。
  2. 同步事件——源于其它任务或中断的事件。比如说,某个任务可以进入阻塞
    态以等待队列中有数据到来。同步事件囊括了所有板级范围内的事件类型。
    FreeRTOS 的队列,二值信号量,计数信号量,互斥信号量(recursive semaphore,
    递归信号量,本文一律称为互斥信号量,因为其主要用于实现互斥访问)和互斥量都可
    以用来实现同步事件。第二章和第三章涵盖了有关这些的详细内容。
    任务可以在进入阻塞态以等待同步事件时指定一个等待超时时间,这样可以有效地
    实现阻塞状态下同时等待两种类型的事件。比如说,某个任务可以等待队列中有数据到
    来,但最多只等 10ms。如果 10ms 内有数据到来,或是 10ms 过去了还没有数据到来,
    这两种情况下该任务都将退出阻塞态。

挂起状态

“挂起(suspended)”也是非运行状态的子状态。处于挂起状态的任务对调度器而言
是不可见的。让一个任务进入挂起状态的唯一办法就是调用 vTaskSuspend() API 函数;
而把一个挂起状态的任务唤 醒的唯一途径就是调用 vTaskResume() 或
vTaskResumeFromISR() API 函数。大多数应用程序中都不会用到挂起状态。

就绪状态

如果任务处于非运行状态,但既没有阻塞也没有挂起,则这个任务处于就绪(ready,
准备或就绪)状态。处于就绪态的任务能够被运行,但只是”准备(ready)”运行,而当前
尚未运行。

vTaskDelay()

void vTaskDelay( portTickType xTicksToDelay ); 

xTicksToDelay 				延迟多少个心跳周期。调用该延迟函数的任务将进入阻塞态,经
							延迟指定的心跳周期数后,再转移到就绪态。
							举个例子,当某个任务调用 vTaskDelay( 100 )时,心跳计数值
							为 10,000,则该任务将保持在阻塞态,直到心跳计数计到
							10,100。
							常数 portTICK_RATE_MS 可以用来将以毫秒为单位的时间值转
							换为以心跳周期为单位的时间值。

demo_test

#if 0
void vTaskFunction( void *pvParameters )
{
	char *pcTaskName;
	volatile unsigned long ul;
	/* 需要打印输出的字符串从入口参数传入。强制转换为字符指针。 */
	pcTaskName = ( char * ) pvParameters;
	/* As per most tasks, this task is implemented in an infinite loop. */
	for( ;; )
	{
		/* Print out the name of this task. */
		vPrintString( pcTaskName );
		/* Delay for a period. */
		for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
		{
	 
		}
	}
}
#endif

void vTaskFunction( void *pvParameters )
{
	char *pcTaskName;
	/* The string to print out is passed in via the parameter. Cast this to a
	   character pointer. */
	pcTaskName = ( char * ) pvParameters;
	/* As per most tasks, this task is implemented in an infinite loop. */
	for( ;; )
	{
		/* Print out the name of this task. */
		vPrintString( pcTaskName );
		/* 延迟一个循环周期。 调用vTaskDelay()以让任务在延迟期间保持在阻塞态。延迟时间以心跳周期为
		   单位,常量portTICK_RATE_MS可以用来在毫秒和心跳周期之间相换转换。本例设定250毫秒的循环周
		   期。 */
		vTaskDelay( 250 / portTICK_RATE_MS );/*调用延时函数使其进入阻塞态,一遍低优先级的task1可以运行,当两个都在阻塞态时,系统会自动调用空闲任务(IDEL)任务,这个方法就
                                                只在程序运行时占用cpu,比跑for(;;)占用的时间少很多*/
	}
}



/* 定义将要通过任务参数传递的字符串。定义为const,且不是在栈空间上,以保证任务执行时也有效。 */
static const char *pcTextForTask1 = “Task 1 is running\r\n”;
static const char *pcTextForTask2 = “Task 2 is running\t\n”;
int main( void )
{
	/* Create one of the two tasks. */
	xTaskCreate( vTaskFunction, /* 指向任务函数的指针. */
			"Task 1", /* 任务名. */
			1000, /* 栈深度. */
			(void*)pcTextForTask1, /* 通过任务参数传入需要打印输出的文本. */
			1, /* 此任务运行在优先级1上. */
			NULL ); /* 不会用到此任务的句柄. */
	/* 同样的方法创建另一个任务。至此,由相同的任务代码(vTaskFunction)创建了多个任务,仅仅是传入
	   的参数不同。同一个任务创建了两个实例。 */
	xTaskCreate( vTaskFunction, "Task 2", 1000, (void*)pcTextForTask2, 2, NULL );

	vTaskStartScheduler();
	for( ;; );
}

在这里插入图片描述

vTaskDelayUntil() API 函数

vTaskDelayUntil()类似于 vTaskDelay()。和范例中演示的一样,函数 vTaskDelay()
的参数用来指定任务在调用 vTaskDelay()到切出阻塞态整个过程包含多少个心跳周期。
任务保持在阻塞态的时间量由 vTaskDelay()的入口参数指定,但任务离开阻塞态的时刻
实际上是相对于 vTaskDelay()被调用那一刻的。vTaskDelayUntil()的参数就是用来指定
任务离开阻塞态进入就绪态那一刻的精确心跳计数值
。API 函数 vTaskDelayUntil()可以
用于实现一个固定执行周期的需求(当你需要让你的任务以固定频率周期性执行的时
候)。由于调用此函数的任务解除阻塞的时间是绝对时刻,比起相对于调用时刻的相对
时间更精确(即比调用 vTaskDelay()可以实现更精确的周期性)

void vTaskDelayUntil( portTickType * pxPreviousWakeTime, portTickType xTimeIncrement );
pxPreviousWakeTime 		此参数命名时假定 vTaskDelayUntil()用于实现某个任务以
						固定频率周期性执行。这种情况下 pxPreviousWakeTime
						保存了任务上一次离开阻塞态(被唤醒)的时刻。这个时刻
						被用作一个参考点来计算该任务下一次离开阻塞态的时
						刻。
						pxPreviousWakeTime 	指向的变量值会在 API 函 数
						vTaskDelayUntil()调用过程中自动更新,应用程序除了该
						变量第一次初始化外,通常都不要修改它的值。程序清单
						14 展示了这个参数的使用方法。
xTimeIncrement 			此参数命名时同样是假定 vTaskDelayUntil()用于实现某个
						任务以固定频率周期性执行 —— 这个频率就是由
						xTimeIncrement 指定的。
						xTimeIncrement 的单位是心跳周期,可以使用常量
						portTICK_RATE_MS 将毫秒转换为心跳周期。

demo_test

#if 0
void vTaskFunction( void *pvParameters )
{
	char *pcTaskName;
	/* The string to print out is passed in via the parameter. Cast this to a
	   character pointer. */
	pcTaskName = ( char * ) pvParameters;
	/* As per most tasks, this task is implemented in an infinite loop. */
	for( ;; )
	{
		/* Print out the name of this task. */
		vPrintString( pcTaskName );
		/* 延迟一个循环周期。 调用vTaskDelay()以让任务在延迟期间保持在阻塞态。延迟时间以心跳周期为
		   单位,常量portTICK_RATE_MS可以用来在毫秒和心跳周期之间相换转换。本例设定250毫秒的循环周
		   期。 */
		vTaskDelay( 250 / portTICK_RATE_MS );
	}
}
#endif

void vTaskFunction( void *pvParameters )
{
	char *pcTaskName;
	portTickType xLastWakeTime;
    /*
        #define portTickType TickType_t
            #if( configUSE_16_BIT_TICKS == 1 )
            typedef uint16_t TickType_t;
            #define portMAX_DELAY ( TickType_t ) 0xffff
            #else
	typedef uint32_t TickType_t;
    */
	/* The string to print out is passed in via the parameter. Cast this to a
	   character pointer. */
	pcTaskName = ( char * ) pvParameters;
	/* 变量xLastWakeTime需要被初始化为当前心跳计数值。说明一下,这是该变量唯一一次被显式赋值。之后,
	   xLastWakeTime将在函数vTaskDelayUntil()中自动更新。 */
	xLastWakeTime = xTaskGetTickCount();//获取当前在心跳值
    /* 变量xLastWakeTime需要被初始化为当前心跳计数值。说明一下,这是该变量唯一一次被显式赋值。之后,
        xLastWakeTime将在函数vTaskDelayUntil()中自动更新。 */
	/* As per most tasks, this task is implemented in an infinite loop. */
	for( ;; )
	{
		/* Print out the name of this task. */
		vPrintString( pcTaskName );
		/* 本任务将精确的以250毫秒为周期执行。同vTaskDelay()函数一样,时间值是以心跳周期为单位的,
		   可以使用常量portTICK_RATE_MS将毫秒转换为心跳周期。变量xLastWakeTime会在
		   vTaskDelayUntil()中自动更新,因此不需要应用程序进行显示更新。 */
		vTaskDelayUntil( &xLastWakeTime, ( 250 / portTICK_RATE_MS ) );
	}
}


/* 定义将要通过任务参数传递的字符串。定义为const,且不是在栈空间上,以保证任务执行时也有效。 */
static const char *pcTextForTask1 = “Task 1 is running\r\n”;
static const char *pcTextForTask2 = “Task 2 is running\t\n”;
int main( void )
{
	/* Create one of the two tasks. */
	xTaskCreate( vTaskFunction, /* 指向任务函数的指针. */
			"Task 1", /* 任务名. */
			1000, /* 栈深度. */
			(void*)pcTextForTask1, /* 通过任务参数传入需要打印输出的文本. */
			1, /* 此任务运行在优先级1上. */
			NULL ); /* 不会用到此任务的句柄. */
	/* 同样的方法创建另一个任务。至此,由相同的任务代码(vTaskFunction)创建了多个任务,仅仅是传入
	   的参数不同。同一个任务创建了两个实例。 */
	xTaskCreate( vTaskFunction, "Task 2", 1000, (void*)pcTextForTask2, 2, NULL );
	/* Start the scheduler so our tasks start executing. */
	vTaskStartScheduler();

	for( ;; );
}

合并阻塞与非阻塞任务
之前的范例分别测试了任务以查询方式和阻塞方式工作的系统行为。本例通过合并
这两种方案的执行流程,再次实现具有既定预期的系统行为。
在优先级 1 上创建两个任务。这两个任务只是不停地打印输出字符串,然它什么
事情也不做。
这两个任务没有调用任何可能导致它们进入阻塞态的 API 函数,所以这两个任务
要么处于就绪态,要么处于运行态。具有这种性质的任务被称为”不停处理(或持
续处理,continuous processing)”任务,因为它们总是有事情要做,虽然在本例
中的它们做的事情没什么意义。持续处理任务
第三个任务创建在优先级 2 上,高于另外两个任务的优先级。这个任务虽然也是
打印输出字符串,但它是周期性的,所以调用了 vTaskDelayUntil(),在每两次打
印之间让自己处于阻塞态。

demo_test

void vPeriodicTask( void *pvParameters )
{
	portTickType xLastWakeTime;
	/* 初始化xLastWakeTime,之后会在vTaskDelayUntil()中自动更新。 */
	xLastWakeTime = xTaskGetTickCount();
	/* As per most tasks, this task is implemented in an infinite loop. */
	for( ;; )
	{
		/* Print out the name of this task. */
		vPrintString( "Periodic task is running\r\n" );
		/* The task should execute every 10 milliseconds exactly. */
		vTaskDelayUntil( &xLastWakeTime, ( 10 / portTICK_RATE_MS ) );
	}
}

void vContinuousProcessingTask( void *pvParameters )
{
	char *pcTaskName;
	/* 打印输出的字符串由任务参数传入,强制转换为char* */
	pcTaskName = ( char * ) pvParameters;
	/* As per most tasks, this task is implemented in an infinite loop. */
	for( ;; )
	{
		/* 打印输出任务名,无阻塞,也无延迟。 */
		vPrintString( pcTaskName );
	}
}



/* 定义将要通过任务参数传递的字符串。定义为const,且不是在栈空间上,以保证任务执行时也有效。 */
static const char *pcTextForTask1 = “Task 1 is running\r\n”;
static const char *pcTextForTask2 = “Task 2 is running\t\n”;
int main( void )
{
	/* Create one of the two tasks. */
	xTaskCreate( vContinuousProcessingTask, /* 指向任务函数的指针. */
			"continuous 1", /* 任务名. */
			1000, /* 栈深度. */
			(void*)pcTextForTask1, /* 通过任务参数传入需要打印输出的文本. */
			1, /* 此任务运行在优先级1上. */
			NULL ); /* 不会用到此任务的句柄. */
	/* 同样的方法创建另一个任务。至此,由相同的任务代码(vTaskFunction)创建了多个任务,仅仅是传入
	   的参数不同。同一个任务创建了两个实例。 */
	xTaskCreate( vContinuousProcessingTask, "continuous 2", 1000, (void*)pcTextForTask2, 1, NULL );
	
	xTaskCreate( vPeriodicTask, "vPeriodicTask", 1000, (void*)pcTextForTask2, 2, NULL );
	
	/* Start the scheduler so our tasks start executing. */
	vTaskStartScheduler();
	/* If all is well then main() will never reach here as the scheduler will
	   now be running the tasks. If main() does reach here then it is likely that
	   there was insufficient heap memory available for the idle task to be created.
	   CHAPTER 5 provides more information on memory management. */
	for( ;; );
}

空闲任务与空闲任务钩子函数

空闲任务拥有最低优先级(优先级 0)以保证其不会妨碍具有更高优先级的应用任务
进入运行态

空闲任务钩子函数的实现限制
空闲任务钩子函数必须遵从以下规则

  1. 绝不能阻或挂起。空闲任务只会在其它任务都不运行时才会被执行(除非有应用任
    务共享空闲任务优先级)。以任何方式阻塞空闲任务都可能导致没有任务能够进入
    运行态!
  2. 如果应用程序用到了 vTaskDelete() AP 函数,则空闲钩子函数必须能够尽快返回。
    因为在任务被删除后,空闲任务负责回收内核资源。如果空闲任务一直运行在钩
    子函数中,则无法进行回收工作
#include<stdio.h>

unsigned long ulIdkeCycleCount =0;

void vApplicationIdleHook(void)
{
    ulIdkeCycleCount++;//用于记录空闲任务执行时间,可在这个这个时间内做其他的操作
}

void vTaskFunction( void *pvParameters )
{
    char *pcTaskName;
	portTickType xLastWakeTime;

    pcTaskName = (char *)pvParameters;
    //xLastWakeTime = xTaskGetTickCount();//获取当前在心跳值,这是唯一的一次初始化,之后的xLastWakeTime的值都会在下面自动更新;
    for(;;){
        vPrintStringAndNumber(pcTaskName,ulIdkeCycleCount);
        vTaskDelay( 250 / portTICK_RATE_MS );
        //vTaskDelayUntil( xLastWakeTime,250 / portTICK_RATE_MS );
    }
}


const char *pcTextForTask1 = "task1Run\r\n";
const char *pcTextForTask2 = "task2Run\r\n";
int main(void)
{
    xTaskCreat(vTaskFunction,"Vtask1",configMINIMAL_STACK_SIZE * 4,pcTextForTask1,configMAX_PRIORITIES - 1,NULL);
    xTaskCreat(vTaskFunction,"Vtask2",configMINIMAL_STACK_SIZE * 4,pcTextForTask2,configMAX_PRIORITIES - 1,NULL);
    xTaskCreat(vApplicationIdleHook,"Idle",configMINIMAL_STACK_SIZE * 4,NULL,0,NULL);

    vTaskStartScheduler();
    for(;;);

}

在这里插入图片描述

改变任务优先级

vTaskPrioritySet() API 函数
vTaskPriofitySet()可以用于在调度器启动后改变任何任务的优先级。

void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority );
pxTask 			被修改优先级的任务句柄(即目标任务)——参考 xTaskCreate() API
				函数的参数 pxCreatedTask 以了解如何得到任务句柄方面的信息。
				任务可以通过传入 NULL 值来修改自己的优先级。
uxNewPriority 	目标任务将被设置到哪个优先级上。如果设置的值超过了最大可用优
				先级(configMAX_PRIORITIES – 1),则会被自动封顶为最大值。常
				量 configMAX_PRIORITIES 是在 FreeRTOSConfig.h 头文件中设置
				的一个编译时选项。

uxTaskPriorityGet()//获取任务优先级

pxTask 		被查询任务的句柄(目标任务) ——参考 xTaskCreate() API 函数的参数
			pxCreatedTask 以了解如何得到任务句柄方面的信息。
			任务可以通过传入 NULL 值来查询自己的优先级。
			返回值 被查询任务的当前优先级。

task
1.任务 1(程序清单 22)创建在最高优先级,以保证其可以最先运行。任务 1 首先
打印输出两个字符串,然后将任务 2(程序清单 23)的优先级提升到自己之上。
2. 任务 2 一旦拥有最高优先级便启动执行(进入运行态)。由于任何时候只可能有
一个任务处于运行态,所以当任务 2 运行时,任务 1 处于就绪态。
3. 任务 2 打印输出一个信息,然后把自己的优先级设回低于任务 1 的初始值。
4. 任务 2 降低自己的优先级意味着任务 1 又成为具有最高优先级的任务,所以任
务 1 重新进入运行态,任务 2 被强制切入就绪态。

/*
    void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority );
    pxTask          被修改优先级的任务句柄(即目标任务)——参考 xTaskCreate() API
                    函数的参数 pxCreatedTask 以了解如何得到任务句柄方面的信息。
                    任务可以通过传入 NULL 值来修改自己的优先级。
    
    uxNewPriority   目标任务将被设置到哪个优先级上。如果设置的值超过了最大可用优
                    先级(configMAX_PRIORITIES – 1),则会被自动封顶为最大值。常
                    量 configMAX_PRIORITIES 是在 FreeRTOSConfig.h 头文件中设置
                    的一个编译时选项。

    uxTaskPriorityGet() API 函数
        uxTaskPriorityGet() API 函数用于查询一个任务的优先级。传入NULL 用来查询自身优先级
        pxTask  被查询任务的句柄(目标任务) ——参考 xTaskCreate() API 函数的参数
                pxCreatedTask 以了解如何得到任务句柄方面的信息。
                任务可以通过传入 NULL 值来查询自己的优先级。
        返回值   被查询任务的当前优先级。
*/

#include<stdio.h>

unsigned long ulIdkeCycleCount =0;
/* 声明变量用于保存任务2的句柄。 */ 
xTaskHandle xTask2Handle;


void vTaskFunction2( void *pvParameters )
{
    char *pcTaskName;
	uxPriority = uxTaskPriorityGet( NULL );

    pcTaskName = (char *)pvParameters;
    //xLastWakeTime = xTaskGetTickCount();//获取当前在心跳值,这是唯一的一次初始化,之后的xLastWakeTime的值都会在下面自动更新;
    for(;;){
        vPrintString(pcTaskName);

        vTaskPrioritySet(NULL,   uxPriority-2 );        
    }
}

void vTaskFunction1( void *pvParameters )
{
    char *pcTaskName;
	uxPriority = uxTaskPriorityGet( NULL );

    pcTaskName = (char *)pvParameters;
    //xLastWakeTime = xTaskGetTickCount();//获取当前在心跳值,这是唯一的一次初始化,之后的xLastWakeTime的值都会在下面自动更新;
    for(;;){
        vPrintString(pcTaskName);
        
        vTaskPrioritySet(  xTask2Handle,   uxPriority+2 );
        
    }
}


const char *pcTextForTask1 = "task1Run\r\n";
const char *pcTextForTask2 = "task2Run\r\n";
int main(void)
{
    xTaskCreat(vTaskFunction1,"Vtask1",configMINIMAL_STACK_SIZE * 4,pcTextForTask1,2,NULL);
    xTaskCreat(vTaskFunction2,"Vtask2",configMINIMAL_STACK_SIZE * 4,pcTextForTask2,1,&xTask2Handle);

    vTaskStartScheduler();
    for(;;);

}


const char *pcTextForTask1 = "task1Run\r\n";
const char *pcTextForTask2 = "task2Run\r\n";
int main(void)
{
    xTaskCreat(vTaskFunction1,"Vtask1",configMINIMAL_STACK_SIZE * 4,pcTextForTask1,2,NULL);
    xTaskCreat(vTaskFunction2,"Vtask2",configMINIMAL_STACK_SIZE * 4,pcTextForTask2,1,&xTask2Handle);

    vTaskStartScheduler();
    for(;;);

}

删除任务

vTaskDelete() API 函数
任务可以使用 API 函数 vTaskDelete()删除自己或其它任务。
任务被删除后就不复存在,也不会再进入运行态。
空闲任务的责任是要将分配给已删除任务的内存释放掉。因此有一点很重要,那就
是使用 vTaskDelete() API 函数的任务千万不能把空闲任务的执行时间饿死。

需要说明一点,只有内核为任务分配的内存空间才会在任务被删除后自动回收。任
务自己占用的内存或资源需要由应用程序自己显式地释放

void vTaskDelete( xTaskHandle pxTaskToDelete ); 

pxTaskToDelete 	被删除任务的句柄(目标任务) —— 参考 xTaskCreate() API 函数的
				参数 pxCreatedTask 以了解如何得到任务句柄方面的信息。
				任务可以通过传入 NULL 值来删除自己。

demo_test

/*
        vTaskDelete() API 函数
                任务可以使用 API 函数 vTaskDelete()删除自己或其它任务。
                任务被删除后就不复存在,也不会再进入运行态。
                空闲任务的责任是要将分配给已删除任务的内存释放掉。因此有一点很重要,那就
                是使用 vTaskDelete() API 函数的任务千万不能把空闲任务的执行时间饿死。
                需要说明一点,只有内核为任务分配的内存空间才会在任务被删除后自动回收。任务自己占用的内存或资源需要由应用程序自己显式地释放。

        void vTaskDelete( xTaskHandle pxTaskToDelete ); 
        
            pxTaskToDelete 被删除任务的句柄(目标任务) —— 参考 xTaskCreate() API 函数的
            参数 pxCreatedTask 以了解如何得到任务句柄方面的信息。
            任务可以通过传入 NULL 值来删除自己。
*/
#include<stdio.h>

void vTaskFunction2( void *pvParameters );
void vTaskFunction1( void *pvParameters );

const char *pcTextForTask1 = "task1Run\r\n";
const char *pcTextForTask2 = "task2Run\r\n";
xTaskHandle xTask2Handle;

int main(void)
{
    xTaskCreat(vTaskFunction1,"Vtask1",configMINIMAL_STACK_SIZE * 4,pcTextForTask1,1,NULL);

    vTaskStartScheduler();
    for(;;);

}


void vTaskFunction2( void *pvParameters )
{
    //char *pcTaskName;
	uxPriority = uxTaskPriorityGet( NULL );

    //pcTaskName = (char *)pvParameters;
    //xLastWakeTime = xTaskGetTickCount();//获取当前在心跳值,这是唯一的一次初始化,之后的xLastWakeTime的值都会在下面自动更新;
    for(;;){
        vPrintString("Task2 run and delete self\r\n");
        vTaskDelete(xTask2Handle);
      
    }
}

void vTaskFunction1( void *pvParameters )
{
    char *pcTaskName;
	uxPriority = uxTaskPriorityGet( NULL );

    pcTaskName = (char *)pvParameters;
    //xLastWakeTime = xTaskGetTickCount();//获取当前在心跳值,这是唯一的一次初始化,之后的xLastWakeTime的值都会在下面自动更新;
    for(;;){
        vPrintString(pcTaskName);
        
         xTaskCreat(vTaskFunction2,"Vtask2",configMINIMAL_STACK_SIZE * 4,NULL,2,xTask2Handle);

         vTaskDelay( 100 / portTICK_RATE_MS );
        
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值