void vTask1( void *pvParameters )
{
const char *pcTaskName = "Task 1 is running\r\n";
volatile unsigned long ul;
/* 和大多数任务一样,该任务处于一个死循环中。 */
for( ;; )
{
/* Print out the name of this task. */
vPrintString( pcTaskName );
/* 延迟,以产生一个周期 */
for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
{
/* 这个空循环是最原始的延迟实现方式。在循环中不做任何事情。后面的示例程序将采用
delay/sleep函数代替这个原始空循环。 */
}
}
}
void vTask2( void *pvParameters )
{
const char *pcTaskName = "Task 2 is running\r\n";
volatile unsigned long ul;
/* 和大多数任务一样,该任务处于一个死循环中。 */
for( ;; )
{
/* Print out the name of this task. */
vPrintString( pcTaskName );
/* 延迟,以产生一个周期 */
for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
{
/* 这个空循环是最原始的延迟实现方式。在循环中不做任何事情。后面的示例程序将采用
delay/sleep函数代替这个原始空循环。 */
}
}
}
int main( void )
{
/* 创建第一个任务。需要说明的是一个实用的应用程序中应当检测函数xTaskCreate()的返回值,以确保任
务创建成功。 */
xTaskCreate( vTask1, /* 指向任务函数的指针 */
"Task 1", /* 任务的文本名字,只会在调试中用到 */
1000, /* 栈深度 – 大多数小型微控制器会使用的值会比此值小得多 */
NULL, /* 没有任务参数 */
1, /* 此任务运行在优先级1上. */
NULL ); /* 不会用到任务句柄 */
/* Create the other task in exactly the same way and at the same priority. */
xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
/* 启动调度器,任务开始执行 */
vTaskStartScheduler();
/* 如果一切正常,main()函数不应该会执行到这里。但如果执行到这里,很可能是内存堆空间不足导致空闲
任务无法创建。第五章有讲述更多关于内存管理方面的信息 */
for( ;; );
}
在任务1种创建任务2
void vTask1( void *pvParameters )
{
const char *pcTaskName = "Task 1 is running\r\n";
volatile unsigned long ul;
/* 如果已经执行到本任务的代码,表明调度器已经启动。在进入死循环之前创建另一个任务。 */
xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
for( ;; )
{
/* Print out the name of this task. */
vPrintString( pcTaskName );
/* Delay for a period. */
for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
{
/* This loop is just a very crude delay implementation. There is
nothing to do in here. Later examples will replace this crude
loop with a proper delay/sleep function. */
}
}
}
用一个任务函数代替任务1任务2
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++ )
{
/* This loop is just a very crude delay implementation. There is
nothing to do in here. Later exercises will replace this crude
loop with a proper delay/sleep function. */
}
}
}
/* 定义将要通过任务参数传递的字符串。定义为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, 1, 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( ;; );
}
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 );
}
}
vTaskDelayUntil函数的使用
void vTaskFunction( void *pvParameters )
{
char *pcTaskName;
portTickType xLastWakeTime;
/* 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();
/* 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 ) );
}
}
定义一个空闲任务钩子函数
/* Declare a variable that will be incremented by the hook function. */
unsigned long ulIdleCycleCount = 0UL;
/* 空闲钩子函数必须命名为vApplicationIdleHook(),无参数也无返回值。 */
void vApplicationIdleHook( void )
{
/* This hook function does nothing but increment a counter. */
ulIdleCycleCount++;
}
改变任务优先级
通过调用 vTaskPrioritySet() API 函数来改变两个任务的相对优先级
void vTask1( void *pvParameters )
{
unsigned portBASE_TYPE uxPriority;
/* 本任务将会比任务2更先运行,因为本任务创建在更高的优先级上。任务1和任务2都不会阻塞,所以两者要
么处于就绪态,要么处于运行态。
查询本任务当前运行的优先级 – 传递一个NULL值表示说“返回我自己的优先级”。 */
uxPriority = uxTaskPriorityGet( NULL );
for( ;; )
{
/* Print out the name of this task. */
vPrintString( "Task1 is running\r\n" );
/* 把任务2的优先级设置到高于任务1的优先级,会使得任务2立即得到执行(因为任务2现在是所有任务
中具有最高优先级的任务)。注意调用vTaskPrioritySet()时用到的任务2的句柄。程序清单24将展示
如何得到这个句柄。 */
vPrintString( "About to raise the Task2 priority\r\n" );
vTaskPrioritySet( xTask2Handle, ( uxPriority + 1 ) );
/* 本任务只会在其优先级高于任务2时才会得到执行。因此,当此任务运行到这里时,任务2必然已经执
行过了,并且将其自身的优先级设置回比任务1更低的优先级。 */
}
}
void vTask2( void *pvParameters )
{
unsigned portBASE_TYPE uxPriority;
/* 任务1比本任务更先启动,因为任务1创建在更高的优先级。任务1和任务2都不会阻塞,所以两者要么处于
就绪态,要么处于运行态。
查询本任务当前运行的优先级 – 传递一个NULL值表示说“返回我自己的优先级”。 */
uxPriority = uxTaskPriorityGet( NULL );
for( ;; )
{
/* 当任务运行到这里,任务1必然已经运行过了,并将本身务的优先级设置到高于任务1本身。 */
vPrintString( "Task2 is running\r\n" );
/* 将自己的优先级设置回原来的值。传递NULL句柄值意味“改变我己自的优先级”。把优先级设置到低
于任务1使得任务1立即得到执行 – 任务1抢占本任务。 */
vPrintString( "About to lower the Task2 priority\r\n" );
vTaskPrioritySet( NULL, ( uxPriority - 2 ) );
}
}
/* 声明变量用于保存任务2的句柄。 */
xTaskHandle xTask2Handle;
int main( void )
{
/* 任务1创建在优先级2上。任务参数没有用到,设为NULL。任务句柄也不会用到,也设为NULL */
xTaskCreate( vTask1, "Task 1", 1000, NULL, 2, NULL );
/* The task is created at priority 2 ______^. */
/* 任务2创建在优先级1上 – 此优先级低于任务1。任务参数没有用到,设为NULL。但任务2的任务句柄会被
用到,故将xTask2Handle的地址传入。 */
xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, &xTask2Handle );
/* The task handle is the last parameter _____^^^^^^^^^^^^^ */
/* Start the scheduler so the 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( ;; );
}
删除任务
其行为如下:
任务
1
则
main()
创建在优先级
1
上。任务
1
运行时,以优先级
2
创建任务
2
。现
在任务
2
具有最高优先级,所以会立即得到执行。
任务
2
什么也没有做,只是删除自己。可以通过传递
NULL
值以
vTaskDelete()
来删除自己,但是为了纯粹的演示,传递的是任务自己的句柄。
当任务
2
被自己删除之后,任务
1
成为最高优先级的任务,所以继续执行,调用
vTaskDelay()
阻塞一小段时间。
当任务
1
进入阻塞状态后,空闲任务得到执行的机会。空闲任务会释放内核为已
删除的任务
2
分配的内存。
任务
1
离开阻塞态后,再一次成为就绪态中具有最高优先级的任务,因此会抢占
空闲任务。又再一次创建任务
2
,如此往复。
int main( void )
{
/* 任务1创建在优先级1上 */
xTaskCreate( vTask1, "Task 1", 1000, NULL, 1, NULL );
/* The task is created at priority 1 ______^. */
/* Start the scheduler so the tasks start executing. */
vTaskStartScheduler();
/* main() should never reach here as the scheduler has been started. */
for( ;; );
}
void vTask1( void *pvParameters )
{
const portTickType xDelay100ms = 100 / portTICK_RATE_MS;
for( ;; )
{
/* Print out the name of this task. */
vPrintString( "Task1 is running\r\n" );
/* 创建任务2为最高优先级。 */
xTaskCreate( vTask2, "Task 2", 1000, NULL, 2, &xTask2Handle );
/* The task handle is the last parameter _____^^^^^^^^^^^^^ */
/* 因为任务2具有最高优先级,所以任务1运行到这里时,任务2已经完成执行,删除了自己。任务1得以
执行,延迟100ms */
vTaskDelay( xDelay100ms );
}
}
void vTask2( void *pvParameters )
{
/* 任务2什么也没做,只是删除自己。删除自己可以传入NULL值,这里为了演示,还是传入其自己的句柄。 */
vPrintString( "Task2 is running and about to delete itself\r\n" );
vTaskDelete( xTask2Handle );
}