freertos任务的定义与任务切换

一、任务

在裸机系统中,系统的主体就是main()函数里面顺序执行的无限循环,CPU按照循序完成哥哥操作。
在多任务系统中,根据功能的不同,把整个系统分割成一个独立的且无法返回的函数。

二、创建任务

在多任务系统中,每个任务都是独立的,互不干扰的,所以要为每个任务都分配独立的栈空间,这个栈空间通常是一个预先定义好的全局数组,也可以是动态分配的一段内存空间,但它们都存在于 RAM 中。

2.1 创建任务栈

1 #define TASK1_STACK_SIZE 128 (2)
2 StackType_t Task1Stack[TASK1_STACK_SIZE]; (1)
3 
4 #define TASK2_STACK_SIZE 128
5 StackType_t Task2Stack[TASK2_STACK_SIZE];

2.2 定义任务函数

main.c

1 /* 软件延时 */
2 void delay (uint32_t count)
3 {
4    for (; count!=0; count--);
5 }
6 /* 任务 1 */
7 void Task1_Entry( void *p_arg ) (1)
8 {
9    for ( ;; )
10   {
11     flag1 = 1;
12     delay( 100 );
13     flag1 = 0;
14     delay( 100 );
15    }
16 }
17 
18 /* 任务 2 */
19 void Task2_Entry( void *p_arg ) (2)
20   {
21   for ( ;; )
22   {
23     flag2 = 1;
24     delay( 100 );
25     flag2 = 0;
26     delay( 100 );
27   }
28 }

2.3 定义任务控制块

在多任务系统中,任务的执行是由系统调度的。系统为了顺利的调度任务,为每个任务都额外定义了一个任务控制块,这个任务控制块就相当于任务的身份证,里面存有任务的所有信息,比如任务的栈指针,任务名称,任务的形参等。有了这个任务控制块之后,以后系统对任务的全部操作都可以通过这个任务控制块来实现

1 typedef struct tskTaskControlBlock
2 {
3   volatile StackType_t *pxTopOfStack; /* 栈顶 */ (1)
4 
5   ListItem_t xStateListItem; /* 任务节点 */ (2)
6 
7   StackType_t *pxStack; /* 任务栈起始地址 */ (3)
8   /* 任务名称,字符串形式 */(4)
9   char pcTaskName[ configMAX_TASK_NAME_LEN ];
10 } tskTCB;

2.4 实现任务创建函数

任务的栈,任务的函数实体,任务的控制块最终需要联系起来才能由系统进行统一调度。那么这个联系的工作就由任务创建函数 xTaskCreateStatic()来实现。

任务的创建有两种方法,一种是使用动态创建,一种是使用静态创建动态创建时,任务控制块和栈的内存是创建任务时动态分配的,任务删除时,内存可以释放静态创建时,任务控制块和栈的内存需要事先定义好,是静态的内 存 , 任 务 删 除 时 , 内 存 不 能 释 放 。 目 前 我 们 以 静 态 创 建 为 例 来 讲 解 ,configSUPPORT_STATIC_ALLOCATION 在 FreeRTOSConfig.h 中定义,我们配置为 1。

静态创建:

xTaskCreateStatic()函数 任务创建函数
prvInitialiseNewTask()函数 创建新任务函数

三、实现就绪列表

3.1 定义就绪列表

任务创建好之后,我们需要把任务添加到就绪列表里面,表示任务已经就绪,系统随时可以调度

1 /* 任务就绪列表 */
2 List_t pxReadyTasksLists[ configMAX_PRIORITIES ];

就绪列表实际上就是一个 List_t 类型的数组,数组的大小由决定最 大 任 务 优 先 级 的 宏 configMAX_PRIORITIES 决 定 , configMAX_PRIORITIES 在FreeRTOSConfig.h 中默认定义为 5,最大支持 256 个优先级。数组的下标对应了任务的优先级,同一优先级的任务统一插入到就绪列表的同一条链表中。

3.2 就绪列表初始化
就绪列表初始化的工作在函数prvInitialiseTaskLists()里面实现

将任务插入到就绪列表:任务控制块里面有一个 xStateListItem 成员,数据类型为 ListItem_t,我们将任务插入到就绪列表里面,就是通过将任务控制块的 xStateListItem 这个节点插入到就绪列表中来实现的。如果把就绪列表比作是晾衣架,任务是衣服,那 xStateListItem 就是晾衣架上面的钩子,每个任务都自带晾衣架钩子,就是为了把自己挂在各种不同的链表中。

1 /* 初始化与任务相关的列表,如就绪列表 */ 
2 prvInitialiseTaskLists(); 
3 
4 Task1_Handle =                                                    /* 任务句柄 */
5                   xTaskCreateStatic( (TaskFunction_t)Task1_Entry, /* 任务入口 */
6                   (char *)"Task1", /* 任务名称,字符串形式 */
7                   (uint32_t)TASK1_STACK_SIZE , /* 任务栈大小,单位为字 */
8                   (void *) NULL, /* 任务形参 */
9                   (StackType_t *)Task1Stack, /* 任务栈起始地址 */
10                  (TCB_t *)&Task1TCB ); /* 任务控制块 */
11 
12 /* 将任务添加到就绪列表 */ 
13 vListInsertEnd( &( pxReadyTasksLists[1] ),  &( ((TCB_t *)(&Task1TCB))->xStateListItem ) ); 
15 
16 Task2_Handle = /* 任务句柄 */
17                    xTaskCreateStatic( (TaskFunction_t)Task2_Entry, /* 任务入口 */
18                    (char *)"Task2", /* 任务名称,字符串形式 */
19                    (uint32_t)TASK2_STACK_SIZE , /* 任务栈大小,单位为字 */
20                    (void *) NULL, /* 任务形参 */
21                    (StackType_t *)Task2Stack, /* 任务栈起始地址 */
22                    (TCB_t *)&Task2TCB ); /* 任务控制块 */
23 /* 将任务添加到就绪列表 */ 
24 vListInsertEnd( &( pxReadyTasksLists[2] ), &( ((TCB_t *)(&Task2TCB))->xStateListItem ) );

由于这里是静态创建,所以任务的优先级是数组的下标。

四、实现调度器

调度器是操作系统的核心,其主要功能就是实现任务的切换,即从就绪列表里面找到优先级最高的任务,然后去执行该任务。

4.1 启动调度器
调度器的启动由 vTaskStartScheduler()函数来完成。

4.2 任务切换
任务切换就是在就绪列表中寻找优先级最高的就绪任务,然后去执行该任务。但是目
前我们还不支持优先级,仅实现两个任务轮流切换,任务切换函数 taskYIELD()具体实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值