FreeRTOS之任务管理

FreeRTOS学习记录第一天

一、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int main(void)
{

/*主控及相关外设初始化*/
BspInit ();

/* 创建任务 */
AppTaskCreate();

/* 启动调度,开始执行任务 */
vTaskStartScheduler();

/* 如果一切正常, main()函数不应该会执行到这里。但如果执行到这里,很可能是内存堆空间不足导致空闲
任务无法创建。第五章有讲述更多关于内存管理方面的信息 */
for( ;; );
}

static void vTaskTest1(void *pvParameters)
{
volatile unsigned long u1;
for(;;)
{

printf("Test 1\r\n");
for(u1=0;u1<1000000;u1++)
{

}

}
}

static void vTaskTest2(void *pvParameters)
{
volatile unsigned long u1;
for(;;)
{
printf("Test 2\r\n");
for(u1=0;u1<1000000;u1++)
{

}

}
}

static void AppTaskCreate (void)
{

/*测试任务*/
xTaskCreate( vTaskTest1, "vTaskTest1", 512, NULL, 1, NULL );
xTaskCreate( vTaskTest2, "vTaskTest2", 512, NULL, 1, NULL );
}

串口输出:
test1.png
分析:
两个任务的优先级相同,因为我们开启了调度器,调度器会让任务轮流执行一个“时间片”;
如果其中一个优先级高,将一直执行优先级高的任务,优先级低的任务也就被“饿死”了,CPU在任何时刻永远执行当前优先级最高的任务;


二、
仅更改任务的优先级:

1
2
3
4
5
6
7
static void AppTaskCreate (void)
{

/*测试任务*/
xTaskCreate( vTaskTest1, "vTaskTest1", 512, NULL, 1, NULL );
xTaskCreate( vTaskTest2, "vTaskTest2", 512, NULL, 2, NULL );
}

串口输出如下图所示:
test3.png


三、
使用vTaskDelay()延时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
static void vTaskTest1(void *pvParameters)
{
volatile unsigned long u1;
for(;;)
{

printf("Test 1\r\n");
/*调用vTaskDelay()让任务在延迟期间保持在阻塞态,延迟是以心跳周期为单位,
常量portTick_RATE_MS可以用来在毫秒和心跳周期之间相互转换。*/
vTaskDelay(250 / portTICK_RATE_MS);

}
}

static void vTaskTest2(void *pvParameters)
{
volatile unsigned long u1;
for(;;)
{
printf("Test 2\r\n");
/*调用vTaskDelay()让任务在延迟期间保持在阻塞态,延迟是以心跳周期为单位,
常量portTick_RATE_MS可以用来在毫秒和心跳周期之间相互转换。*/
vTaskDelay(250 / portTICK_RATE_MS);

}
}

static void AppTaskCreate (void)
{

/*测试任务*/
xTaskCreate( vTaskTest1, "vTaskTest1", 512, NULL, 1, NULL );
xTaskCreate( vTaskTest2, "vTaskTest2", 512, NULL, 2, NULL );
}

串口打印如下:
test4.png
分析:
当程序进入阻塞态时,CPU将执行当前优先级虽然较低但是可以运行的任务,这样来回切换。


四、
补充1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define configUSE_PREEMPTION                     1
#define configSUPPORT_STATIC_ALLOCATION 0
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ( SystemCoreClock )
#define configTICK_RATE_HZ ((TickType_t)1000) /*心跳中断频率为1000HZ也就是时间片长度1ms*/
#define configMAX_PRIORITIES ( 7 )
#define configMINIMAL_STACK_SIZE ((uint16_t)128)
#define configTOTAL_HEAP_SIZE ((size_t)( 30 * 1024 ) )
#define configMAX_TASK_NAME_LEN ( 16 )
#define configUSE_16_BIT_TICKS 0
#define configUSE_MUTEXES 1
#define configQUEUE_REGISTRY_SIZE 8
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
#define configIDLE_SHOULD_YIELD 1

补充2:
vTaskDelayUntil()可以用于精准延时;
函数原型:

1
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )

使用示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void vTaskTest1(void *pvParameters)
{
portTickType xLastWakeTime;
/*变量xLastWakeTime需要被初始化为当前心跳计数值。说明一下,这是该变量
唯一一次被显式赋值,之后,xLastWakeTime将在函数vTaskDelayUntil()中自动
更新*/
xLastWakeTime = xTaskGetTickCount();

for(;;)
{

printf("Test 1\r\n");
/*调用vTaskDelay()让任务在延迟期间保持在阻塞态,延迟是以心跳周期为单位,
常量portTick_RATE_MS可以用来在毫秒和心跳周期之间相互转换。*/
vTaskDelayUntil(&xLastWakeTime,(1000 / portTICK_RATE_MS));


}
}

FreeRTOS学习记录第二天

一、钩子函数
程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
unsigned long ulIdleCycleCount=0UL;//钩子函数变量初始值
static void vTaskTest1(void *pvParameters)
{
for(;;)
{

printf("Test 1\r\n");
printf("ulIdleCycleCount=%ld\r\n",ulIdleCycleCount);
/*调用vTaskDelay()让任务在延迟期间保持在阻塞态,延迟是以心跳周期为单位,
常量portTick_RATE_MS可以用来在毫秒和心跳周期之间相互转换。*/
vTaskDelay(250 / portTICK_RATE_MS);
}
}

static void vTaskTest2(void *pvParameters)
{
volatile unsigned long u1;
for(;;)
{
printf("Test 2\r\n");
printf("ulIdleCycleCount=%ld\r\n",ulIdleCycleCount);
/*调用vTaskDelay()让任务在延迟期间保持在阻塞态,延迟是以心跳周期为单位,
常量portTick_RATE_MS可以用来在毫秒和心跳周期之间相互转换。*/
vTaskDelay(250 / portTICK_RATE_MS);
}
}

/*钩子函数必须命名为vApplicationIdleHook,无参数也无返回值*/
void vApplicationIdleHook( void )
{

ulIdleCycleCount++;

}

串口打印如下:
钩子函数1.png
注:使用钩子函数必须把configUSE_IDLE_HOOK配置为1
钩子函数2.png
分析如下:
空闲任务钩子函数会被空闲任务每循环一次就自动调用一次。


通常空闲任务钩子函数被用于:
1.执行低优先级,后台或需要不停处理的功能代码;
2.测试系统处理裕量;
3.将处理器配置到低功耗模式—提供一种自动省电的方法,使得在没有任何应用功能需要处理的时候,系统自动进入省电模式;


二、改变任务优先级
原型:void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
注:
1.改变任务优先级
2.最高优先级为configMAX_PRIORITIES-1,如果设置的值超过了最大可用优先级,则自动封顶为最大值


原型:UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask )
注:
1.获取任务当前的优先级


程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/*声明变量用于保存任务2的句柄*/
xTaskHandle xTask2Handle;
static void vTaskTest1(void *pvParameters)
{
UBaseType_t uxNewPriority;
/*NULL表示获取“返回我自己的优先级”*/
uxNewPriority=uxTaskPriorityGet(NULL);
for(;;)
{

printf("Test 1\r\n");
printf("About to run Task2\r\n");
/*将任务2的优先级,设置为比任务1的优先级高1也就是3,
如果不改变优先级的话,就将一直运行任务1了*/
vTaskPrioritySet( xTask2Handle, (uxNewPriority+1));
}
}

static void vTaskTest2(void *pvParameters)
{
UBaseType_t uxNewPriority;
/*NULL表示获取“返回我自己的优先级”*/
uxNewPriority=uxTaskPriorityGet(NULL);
for(;;)
{
printf("Test 2\r\n");
printf("About to lower the Task2 priority\r\n");
/*uxNewPriority-2=1即恢复原来的优先级1,任务1得以运行,否则将
一直运行任务2,任务1将被饿死*/
vTaskPrioritySet( NULL, (uxNewPriority-2));

}
}

static void AppTaskCreate (void)
{

/*测试任务*/
/*任务1创建在优先级2上。任务参数没有用到,设为NULL。任务句柄也不会用到,也设为NULL*/
xTaskCreate( vTaskTest1, "vTaskTest1", 512, NULL, 2, NULL );
/*任务2创建在优先级1上,此优先级低于任务1,任务参数没有用到,设为NULL。但任务2的任务
句柄会被用到,故将xTask2Handle的地址传入*/
xTaskCreate( vTaskTest2, "vTaskTest2", 512, NULL, 1,&xTask2Handle );
}

串口打印如下:
更改优先级1.png


三、删除任务
原型:void vTaskDelete( TaskHandle_t xTaskToDelete )
注:
1.只有内核为任务分配的内存空间才会在任务被删除后自动回收。任务自己占用的内存或资源需要由应用程序自己显式地释放。